ホームページ  >  記事  >  バックエンド開発  >  PHP+Redis はキャッシュの故障という実際の問題を解決します

PHP+Redis はキャッシュの故障という実際の問題を解決します

藏色散人
藏色散人転載
2022-01-10 15:20:533736ブラウズ

関連する推奨事項: 「PHP Redis は、注文電流制限という実際的な問題を解決します。

PHP Redis は、実際的な問題を解決します問題: キャッシュの内訳

1. この記事シリーズの各号では、実践的な Redis の問題を解決します
2. 各号の質問は各号のコメントから選択されます
3. 質問Redis 関連に限定されていますが、その他の質問に興味がある場合は、新しいシリーズを開始することも除外しません
4。私はよく PHP を使用するので、解決策は主に PHP
5 です。適切な質問がない場合は、コメントで、私自身の質問をします

問題の説明:

これは 2 番目の問題です。私は今でも自分で質問をしています [Dog Head]

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;
    }

コード解釈:

  • データ本体は期限切れになりません

  • ロックの有効期限を設定します。ロックの有効期限が切れると、ロックを取得したユーザーがデータを更新します

  • #取得されていません ロックに到達したユーザーがデータを取得した場合、データは直接返されます (この時点でデータはすでに期限切れになっています。データの適時性に対する高い要件があるため、コードを自分で変更する必要があります)

  • ロックが取得されていません。ユーザーがデータを取得できない場合、データが取得されているかどうかが 1 秒ごとに判断されます。ロックを取得したユーザーが実行を完了しました。

  • ロックがデッドロックになる可能性は非常に低いです。定期的に処理するスケジュールされたタスク (クリーニングなど) を設定することをお勧めします。毎日のビジネスの閑散期にデッドロックを発生させる

推奨学習: 「PHP ビデオ チュートリアル

以上がPHP+Redis はキャッシュの故障という実際の問題を解決しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。