Home  >  Article  >  Backend Development  >  PHP+Redis solves the actual problem of cache breakdown

PHP+Redis solves the actual problem of cache breakdown

藏色散人
藏色散人forward
2022-01-10 15:20:533726browse

Related recommendations: "PHP Redis solves the practical problem of order current limit"

PHP Redis solves the practical problem: Cache breakdown

1. Each issue of this series of articles will solve a practical Redis problem
2. The questions of each issue will be selected from the comments of each issue
3. The questions are limited to Redis related, others If the question interests me, I won’t rule out starting a new series
4. I often use PHP, so the solution is mainly PHP
5. If there are no suitable questions in the comments, I will make my own questions

Problem description:

This is the second issue, and I still make my own question [Dog Head]

PHP Redis How to simply avoid cache breakdown

Solution:

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

Code interpretation:

  • The data body never expires

  • Set the expiration time of the lock. When the lock expires, the user who grabbed the lock refreshes the data

  • Not grabbed If the user who reaches the lock has obtained the data, it will return directly (the data is already expired at this time. If you have high requirements for data timeliness, you need to modify the code yourself)

  • The lock is not grabbed If the user does not get the data, it will be judged every second whether the user who grabbed the lock has completed the execution.

  • The lock has a very small probability of becoming a deadlock. It is best to have a scheduled task to process it regularly, such as Cleaning up deadlocks during low business periods every day

Recommended study: "PHP Video Tutorial"

The above is the detailed content of PHP+Redis solves the actual problem of cache breakdown. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:learnku.com. If there is any infringement, please contact admin@php.cn delete