推薦(免費):redis
-
緩存穿透:key中對應的緩存數據不存在,導致去請求數據庫,造成數據庫的壓力倍增的情況
-
緩存擊穿:redis過期后的一瞬間,有大量用戶請求同一個緩存數據,導致這些請求都去請求數據庫,造成數據庫壓力倍增的情,針對一個key而言
-
緩存雪崩:緩存服務器宕機或者大量緩存集中某個時間段失效,導致請求全部去到數據庫,造成數據庫壓力倍增的情況,這個是針對多個key而言
一、緩存穿透的解決方案
- 常用方法可以采用布隆過濾器方法進行數據攔截,其次可以還有一種解決思路,就是如果請求的數據為空,將空值也進行緩存,就不會發生穿透情況
<?php class getPrizeList { /** * redis實例 * @var Redis */ private $redis; /** * @var string */ private $redis_key = '|prize_list'; /** * 過期時間 * @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 = '|prize_list'; /** * @var string */ private $setnx_key = '|prize_list_setnx'; /** * 過期時間 * @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 = '|prize_list'; /** * 緩存標記key * @var string */ private $cash_key = '|prize_list_cash'; /** * 過期時間 * @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; } }
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END