我们一起来看一下这张图了解一下我们为什么要这么做缓存处理:
我们先创建本地Yac缓存:
class Cache
{
/**
* @var string
*/
public $prefix = '_yac:';
private $_cache;
public function getCache(){
if(!($this->_cache instanceof \Yac)){
$this->_cache = new \Yac($this->prefix);
}
return $this->_cache;
}
/**
* 针对较长的key做has运算
* @param $key
* @return string
*/
protected function _formatKey($key){
if(strlen($this->prefix.$key) > 48){
return md5($key);
}
return $key;
}
/**
* @param string $key
* @param mixed $value
* @param int $timeOut
* @return mixed
*/
public function set($key,$value,$timeOut=0){
$key = $this->_formatKey($key);
return $this->getCache()->set($key,$value,$timeOut);
}
/**
* @param array $kvs
* @param int $timeOut
* @return mixed
*/
public function mSet(array $kvs,$timeOut=0){
foreach ($kvs as $index => $kv) {
$hashkeys[$this->_formatKey($index)] = $kv;
}
return $this->getCache()->set($hashkeys,$timeOut);
}
/**
* @return mixed
* @param $key
*/
public function get($key){
$key = $this->_formatKey($key);
return $this->getCache()->get($key);
}
/**
* @return mixed
* @param array $keys
*/
public function mget(array $keys){
$hashkeys = [];
foreach($keys as $values){
$hashkeys[$this->_formatKey($values)] = $values;
}
unset($keys);
$keyValues = $this->getCache()->get(array_keys($hashkeys));
$data = [];
foreach ($keyValues as $haskey => $value) {
$data[$hashkeys[$haskey]] = $value;
}
return $data;
}
/**
* @param $keys
* @param int $delay
* @return mixed
*/
public function delete($keys,$delay=0){
if(is_array($keys)){
$hashkeys = array_map(function ($value){
return $this->_formatKey($value);
},$keys);
}else{
$hashkeys = $this->_formatKey($keys);
}
return $this->getCache()->delete($hashkeys,$delay);
}
/**
* 所有缓存都失效
* @return mixed
*/
public function flush(){
return $this->getCache()->flush();
}
/**
* @return mixed
*/
public function info(){
return $this->getCache()->info();
}
}
我们在创建redis缓存:
class RedisCache
{
/**
* Notes:IP
*/
public $host = '127.0.0.1';
/**
* Notes:默认端口
*/
public $port = 6379;
/**
* Notes:加密密码
*/
public $auth = '';
/**
* Notes:默认0库,16个库 0-16
*/
public $db = 0;
/**
* Notes:前缀
*/
public $prefix = 'redis:';
/**
* @var
*/
private $_redis;
public function getRedis()
{
if(!($this->_redis instanceof \Redis)) {
$this->_redis = new \Redis();
$connectResult = $this->_redis->connect($this->host,$this->port);
if(!$connectResult) {
$this->_redis = null;
return false;
}
if(!empty($this->auth)) {
$authResult = $this->_redis->auth($this->auth);
if(!$authResult) {
$this->_redis = null;
return false;
}
}
$this->_redis->select($this->db);
$this->_redis->setOption(\Redis::OPT_PREFIX,$this->prefix);
}
return $this->_redis;
}
/**
* Notes:获取
*/
public function get($key)
{
$values = $this->getRedis()->get($key);
if(empty($values)) {
return false;
}
$values = json_decode($values,true);
return $values['data'];
}
/**
* Notes:存入
*/
public function set($key,$value,$timeout = null)
{
$data = json_encode([
'data' => $value
]);
return $this->getRedis()->set($key,$data,$timeout);
}
现在根据上边的本地yac缓存和redis缓存我们进行处理,我们再一次了解一下原理:
//机制介绍:通过callback方式实现通用缓存机制
//第一步:从yac拉取数据,如果取到数据,校验数据有没有过期,如果没有过期,直接返回数据
//第二步:如果【第一步】没有拉取到数据,或者数据已过期,从redis拉取数据,如果redis没有拉取到数据直接从数据库拉取并更新redis和yac缓存,然后返回数据
//第三步:如果【第二步】redis拉取到数据,且数据没有过期,将redis拉取数据更新到yac缓存,返回redis返回数据(其他服务器更新了redis)
//第四步:如果【第二步】redis拉取到数据,且数据已过期,会获取一个redis锁,如果没有获取到锁,将历史数据返回,如果获取到锁,从callback获取数据并更新yac和redis缓存,最后释放锁
class WaveCache
{
/**
* Notes:数据库值也没有取到值的状态值
*/
public $dbEmpty= -1;
/**
* Notes:wave缓存真实存储时间倍数
*/
public $saveTimes = 3;
/**
* @var
*/
public $localCache;
/**
* @var
*/
public $redisCache;
/**
* @var string
*/
public $lockPrefix = 'redis:lock:';
/**数据是否过期
*/
private function _waveDataIsExpire($data,$time)
{
return $data['logicExpireAt'] <= $time;
}
protected function _waveReturnData($data)
{
//数据格式转化
$data['data'] = ($data['data'] == $this->dbEmpty) ? [] : $data['data'];
return $data['data'];
}
/**波式缓存获取数据
*/
public function waveGet($key,callable $callback,$timeout)
{
$time = time();
//优先使用yac缓存
$yacResult = $this->localCache->get($key);
//本地读取到
if(!empty($yacResult)) {
$isExpire = $this->_waveDataIsExpire($yacResult,$time);
//数据未过期
if(!$isExpire) {
return $this->_waveReturnData($yacResult);
}
}
//yac没有读取到或者已经过期,都需要从redis读取
$redisResult = $this->redisCache->get($key);
//redis没有读取到,直接数据库读取
if(empty($redisResult)) {
return $this->waveSet($key,$callback,$timeout);
}
//redis读取到
$redisIsExpire = $this->_waveDataIsExpire($redisResult,$time);
//数据未过期
if(!$redisIsExpire) {
//其他端更新了redis缓存
$this->localCache->set($key,$redisResult,$timeout);
return $this->_waveReturnData($redisResult);
}
//数据无效,获得锁
$redis = $this->redisCache->getRedis();
$lockKey = $this->lockPrefix.$key;
$lockResult = $redis->multi(\Redis::PIPELINE)
->incr($lockKey)
->expire($lockKey,3)
->exec();
//没有获取到锁,把redis获取结果返回
if($lockResult[0] > 1) {
return $this->_waveReturnData($redisResult);
}
//获取到锁,刷新缓存
$data = $this->waveSet($key,$callback,$timeout);
//释放锁
$redis->del($lockKey);
return $data;
}
/**刷新波式缓存
*/
public function waveSet($key,callable $callback,$timeout)
{
//数据库读取
$result = call_user_func($callback);
//格式化数据
$data = [
'data' => empty($result) ? $this->dbEmpty : $result,
'logicExpireAt' => time()+$timeout,
'timeout' => $timeout
];
$this->localCache->set($key,$data,$timeout);
//更新redis缓存
$this->redisCache->set($key,$data,$this->saveTimes * $timeout);
return $result;
}
/**删除波式缓存
*/
public function waveDel($key)
{
//本地直接删除,以后从redis直接获取
$this->localCache->delete($key);
$result = $this->redisCache->get($key);
if(empty($result)) {
return true;
}
if(isset($result['logicExpireAt'])) {
$result['logicExpireAt'] = 0;
$timeout = isset($result['timeout']) ? (int)$result['timeout'] : 0;
$this->redisCache->set($key,$result,$this->saveTimes * $timeout);
}
return true;
}
}
下边我们来调用一下次方法:
创建一个test.php文件
require __DIR__.'/Cache.php';
require __DIR__.'/RedisCache.php';
require __DIR__.'/WaveCache.php';
$waveCache = new WaveCache();
$localCache = new Cache();
$localCache->prefix = 'wave:yac:';
$waveCache->localCache = $localCache;
$redis = new RedisCache();
$redis->host = '10.20.76.58';
$waveCache->redisCache = $redis;
$menu = $waveCache->waveGet('menu:1',function(){
sleep(1);
return [
'id' => '1',
'name' => 'yac',
'time' => time()
];
},5);
//$beforeDelLocal = $localCache->get('menu:1');
//$beforeDelRedis = $redis->get('menu:1');
//
//var_dump($beforeDelLocal);
//echo PHP_EOL;
//var_dump($beforeDelRedis);die;
//
//$waveCache->waveDel('menu:1');
exit(json_encode([
'get' => $menu,
'beforeDelLocal' => $beforeDelLocal,
'beforeDelRedis' => $beforeDelRedis,
'yac' => $localCache->get('menu:1'),
'redis' => $redis->get('menu:1')
]));
到此波式缓存基本讲完,谢谢大家!如果各位觉得小弟还不错的话,欢迎打赏。
有想学习的同学们可以观看此视频:https://bbs.youkuaiyun.com/topics/392562522 https://edu.youkuaiyun.com/course/play/9933