首頁  >  文章  >  後端開發  >  PHP+Redis解決快取擊穿的實際問題

PHP+Redis解決快取擊穿的實際問題

藏色散人
藏色散人轉載
2022-01-10 15:20:533730瀏覽

相關推薦:《PHP Redis解決訂單限流的實際問題

PHP Redis解決實際問題:快取擊穿

1、本系列文章每期都將解決一個Redis實際問題
2、每期問題將在每期的評論中選取
3、問題限Redis相關,其它問題如果本人感興趣也不排除開闢新系列
4、本人常用PHP所以解決方案以PHP為主
5、評論裡沒有合適的提問時我會自己給自己出題

問題描述:

本期為第二期,依舊是自己出題【狗頭】

PHP Redis如何簡單避免快取擊穿

解決方案:

<?php

    /**
     * 防击穿缓存
     * @param string $key       缓存key
     * @param int $expire       缓存过期时间(s)
     * @param bool $refresh     是否强制刷新数据
     * @param callable $func    获取要缓存的数据的回调方法(仅支持返回类型:?string)
     * @return null|string      返回缓存的值,没有值时且没有抢到刷新权且缓存已过期时返回null
     */
    function onceCache(string $key,int $expire,bool $refresh,callable $func): ?string
    {
        //内存,注:如果是cli模式不要使用
        static $dataStatic=[];

        //读取内存
        if(isset(self::$dataStatic[$key])){
            return self::$dataStatic[$key];
        }

        //获取laravel的获取redis连接,其它框架需要修改
        $redis=Redis::connection();

        //原缓存key加后缀"lock"为锁的key
        $lockKey=$key.&#39;:lock&#39;;

        //锁是否有效
        $lock=false;

        //读取数据
        $data=$redis->get($key);

        //如果 非强制刷新 且 缓存非空 ,获取锁
        if(!$refresh && !is_null($data)){
            $lock = $redis->get($lockKey);
        }

        if(!$lock){//如果锁过期或无数据
            $lock=$redis->setnx($lockKey,1);    //仅当锁不存在时设置锁,值1代表数据获取中
            if($lock || $refresh){              //抢到新锁 或 强制刷新
                try {
                    $data=$func();              //从回调函数获取要缓存的数据

                    //有数据则写入缓存,没有则删除数据
                    if(!is_null($data)){
                        $redis->set($key,$data);
                    }else{
                        $redis->del([$key]);
                    }

                    $redis->set($lockKey,2,&#39;ex&#39;,$expire);   //设置锁的过期时间,值2代表数据获取完成
                }catch (Exception $exception){
                    $redis->del([$lockKey]);                //发生异常,删除锁
                }
            }else{
                //如果没有数据,又没有抢到锁
                //30秒内每秒判断抢到锁的用户是否执行完成,执行完成则从缓存得到数据并返回
                $retry=0;
                do{
                    sleep(1);
                    $retry++;
                    $data = $redis->get($key);
                }while(is_null($data) && $redis->get($lockKey)==1 && $retry<30);
            }
        }

        //写入内存
        if(!is_null($data)){
            $dataStatic[$key]=$data;
        }

        //返回数据
        return $data;
    }

程式碼解讀:

  • ##資料本體永不過期
  • 設定鎖定的過期時間,鎖定過期則搶到鎖定的使用者刷新資料
  • ##未搶到鎖的用戶如果有拿到數據就直接回傳(此時數據已經是過期數據,如果對數據及時性要求高的需要自己改造代碼)
  • 未搶到鎖的使用者如果沒有拿到資料則每秒判斷搶到鎖的使用者是否執行完成
  • 鎖有極小機率變成死鎖,最好有定時任務定期處理,例如每天業務低潮清理死鎖

推薦學習:《PHP影片教學

####

以上是PHP+Redis解決快取擊穿的實際問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除