yac+redis+db做波式缓存

本文介绍了如何结合Yac和Redis构建波式缓存方案。首先,详细阐述了采用这种缓存处理的原因,并展示了创建本地Yac缓存和Redis缓存的步骤。接着,解释了缓存处理的原理,并提供了调用示例代码。最后,分享了一个测试用例及相应的PHP文件,完成了波式缓存的讲解。如需深入了解,可以查看提供的视频教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们一起来看一下这张图了解一下我们为什么要这么做缓存处理:

我们先创建本地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  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值