Maison >base de données >Redis >Apprenez ensemble les principes et les solutions de pénétration du cache Redis, de panne de cache et d'avalanche de cache.

Apprenez ensemble les principes et les solutions de pénétration du cache Redis, de panne de cache et d'avalanche de cache.

coldplay.xixi
coldplay.xixiavant
2021-01-28 18:13:132401parcourir

Apprenez ensemble les principes et les solutions de pénétration du cache Redis, de panne de cache et d'avalanche de cache.

Recommandé (gratuit) : redis

  • Pénétration du cache : Les données de cache correspondantes dans la clé n'existent pas, ce qui entraîne la demande de la base de données, ce qui fait doubler la pression sur la base de données

  • Panne du cache : Un après redis expire En un instant, un grand nombre d'utilisateurs ont demandé les mêmes données mises en cache, ce qui a amené ces requêtes à demander toutes la base de données, ce qui a fait doubler la pression sur la base de données. Pour une clé,

  • <.>Avalanche de cache : Le serveur de cache est en panne ou un grand nombre de caches sont concentrés sur une certaine période de temps, ce qui entraîne l'envoi de toutes les requêtes vers la base de données, ce qui fait doubler la pression de la base de données. Ceci concerne plusieurs clés<.>

1. Solution à la pénétration du cache

  • 常用方法可以采用布隆过滤器方法进行数据拦截,其次可以还有一种解决思路,就是如果请求的数据为空,将空值也进行缓存,就不会发生穿透情况
    <?php
    class getPrizeList {
        /**
         * redis实例
         * @var \Redis
         */
        private $redis;
    
        /**
         * @var string
         */
        private $redis_key = &#39;|prize_list&#39;;
    
        /**
         * 过期时间
         * @var int
         */
        private $expire = 30;
    
        /**
         * getPrizeList constructor.
         * @param $redis
         */
        public function __construct($redis)
        {
            $this->redis = $redis;
        }
    
        /**
         * @return array|bool|string
         */
        public function fetch()
        {
            $result = $this->redis->get($this->redis_key);
            if(!isset($result)) {
                //此处应该进行数据库查询...
                //如果查询结果不存在,给其默认空数组进行缓存
                $result = [];
                $this->redis->set($this->redis_key, $result, $this->expire);
            }
    
            return $result;
        }
    }
2. Solution à la pénétration du cache

  • 使用互斥锁(mutex key),就是一个key过期时,多个请求过来允许其中一个请求去操作数据库,其他请求等待第一个请求成功返回结果后再请求。
    <?php
    class getPrizeList {
        /**
         * redis实例
         * @var \Redis
         */
        private $redis;
    
        /**
         * @var string
         */
        private $redis_key = &#39;|prize_list&#39;;
    
        /**
         * @var string
         */
        private $setnx_key = &#39;|prize_list_setnx&#39;;
    
        /**
         * 过期时间
         * @var int
         */
        private $expire = 30;
    
        /**
         * getPrizeList constructor.
         * @param $redis
         */
        public function __construct($redis)
        {
            $this->redis = $redis;
        }
    
        /**
         * @return array|bool|string
         */
        public function fetch()
        {
            $result = $this->redis->get($this->redis_key);
            if(!isset($result)) {
                if($this->redis->setnx($this->setnx_key, 1, $this->expire)) {
                    //此处应该进行数据库查询...
                    //$result = 数据库查询结果;
                    $this->redis->set($this->redis_key, $result, $this->expire);
                    $this->redis->del($this->setnx_key); //删除互斥锁
                } else {
                    //其他请求每等待10毫秒重新请求一次
                    sleep(10);
                    self::fetch();
                }
            }
    
            return $result;
        }
    }
3. Solution à l'avalanche de cache

  • 这种情况是因为多个key同时过期导致的数据库压力,一种方法可以在key过期时间基础上增加时间随机数,让过期时间分散开,减少缓存时间过期的重复率
  • 另一种方法就是加锁排队,这种有点像上面缓存击穿的解决方式,但是这种请求量太大,比如5000个请求过来,4999个都需要等待,这必然是指标不治本,不仅用户体验性差,分布式环境下就更加复杂,因此在高并发场景下很少使用
  • 最好的解决方法,是使用缓存标记,判断该标记是否过期,过期则去请求数据库,而缓存数据的过期时间要设置的比缓存标记的长,这样当一个请求去操作数据库的时候,其他请求拿的是上一次缓存数据
    <?php
    class getPrizeList {
        /**
         * redis实例
         * @var \Redis
         */
        private $redis;
    
        /**
         * @var string
         */
        private $redis_key = &#39;|prize_list&#39;;
    
        /**
         * 缓存标记key
         * @var string
         */
        private $cash_key = &#39;|prize_list_cash&#39;;
    
        /**
         * 过期时间
         * @var int
         */
        private $expire = 30;
    
        /**
         * getPrizeList constructor.
         * @param $redis
         */
        public function __construct($redis)
        {
            $this->redis = $redis;
        }
    
        /**
         * @return array|bool|string
         */
        public function fetch()
        {
            $cash_result = $this->redis->get($this->cash_key);
            $result = $this->redis->get($this->redis_key);
            if(!$cash_result) {
                $this->redis->set($this->cash_key, 1, $this->expire);
                //此处应该进行数据库查询...
                //$result = 数据库查询结果, 并且设置的时间要比cash_key长,这里设置为2倍;
                $this->redis->set($this->redis_key, $result, $this->expire * 2);
            }
    
            return $result;
        }
    }

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer