首頁 > 資料庫 > Redis > 主體

一起學習 Redis快取穿透、快取擊穿、快取雪崩的原理和解決方法

coldplay.xixi
發布: 2021-01-28 18:13:13
轉載
2341 人瀏覽過

一起學習 Redis快取穿透、快取擊穿、快取雪崩的原理和解決方法

推薦(免費):redis

  • 快取穿透:key中對應的快取資料不存在,導致去請求資料庫,造成資料庫的壓力倍增的情況

  • 快取擊穿:redis過期後的一瞬間,有大量使用者請求同一個快取數據,導致這些請求都去請求資料庫,造成資料庫壓力倍增的情,針對一個key而言

  • ##快取雪崩:快取伺服器宕機或大量快取集中某個時間段失效,導致請求全部去到資料庫,造成資料庫壓力倍增的情況,這個是針對多個key而言

一、快取穿透的解決方案

  • 常用方法可以採用布隆過濾器方法進行資料攔截,其次可以還有一種解決思路,就是如果請求的資料為空,將空值也進行緩存,就不會發生穿透情況
<?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;
    }
}
登入後複製

二、快取擊穿解決方法

  • #使用互斥鎖(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;
    }
}
登入後複製

三、快取雪崩的解決方法

  • 這種情況是因為多個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;
    }
}
登入後複製

以上是一起學習 Redis快取穿透、快取擊穿、快取雪崩的原理和解決方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!