一、緩存穿透
1、什么是緩存穿透
key中對應(yīng)的緩存數(shù)據(jù)不存在,導(dǎo)致去請求數(shù)據(jù)庫,造成數(shù)據(jù)庫的壓力倍增的情況
2、緩存穿透的解決辦法
常用方法可以采用布隆過濾器方法進行數(shù)據(jù)攔截,其次可以還有一種解決思路,就是如果請求的數(shù)據(jù)為空,將空值也進行緩存,就不會發(fā)生穿透情況
<?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)) {
//此處應(yīng)該進行數(shù)據(jù)庫查詢...
//如果查詢結(jié)果不存在,給其默認空數(shù)組進行緩存
$result = [];
$this->redis->set($this->redis_key, $result, $this->expire);
}
return $result;
}
}
二、緩存擊穿
1、什么是緩存擊穿
redis過期后的一瞬間,有大量用戶請求同一個緩存數(shù)據(jù),導(dǎo)致這些請求都去請求數(shù)據(jù)庫,造成數(shù)據(jù)庫壓力倍增的情,針對一個key而言
2、緩存擊穿解決辦法
使用互斥鎖(mutex key),就是一個key過期時,多個請求過來允許其中一個請求去操作數(shù)據(jù)庫,其他請求等待第一個請求成功返回結(jié)果后再請求。
<?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)) {
//此處應(yīng)該進行數(shù)據(jù)庫查詢...
//$result = 數(shù)據(jù)庫查詢結(jié)果;
$this->redis->set($this->redis_key, $result, $this->expire);
$this->redis->del($this->setnx_key); //刪除互斥鎖
} else {
//其他請求每等待10毫秒重新請求一次
sleep(10);
self::fetch();
}
}
return $result;
}
}
三、緩存雪崩
1、什么是緩存雪崩
緩存服務(wù)器宕機或者大量緩存集中某個時間段失效,導(dǎo)致請求全部去到數(shù)據(jù)庫,造成數(shù)據(jù)庫壓力倍增的情況,這個是針對多個key而言
2、緩存雪崩的解決辦法
這種情況是因為多個key同時過期導(dǎo)致的數(shù)據(jù)庫壓力,一種方法可以在key過期時間基礎(chǔ)上增加時間隨機數(shù),讓過期時間分散開,減少緩存時間過期的重復(fù)率
另一種方法就是加鎖排隊,這種有點像上面緩存擊穿的解決方式,但是這種請求量太大,比如5000個請求過來,4999個都需要等待,這必然是指標不治本,不僅用戶體驗性差,分布式環(huán)境下就更加復(fù)雜,因此在高并發(fā)場景下很少使用
最好的解決方法,是使用緩存標記,判斷該標記是否過期,過期則去請求數(shù)據(jù)庫,而緩存數(shù)據(jù)的過期時間要設(shè)置的比緩存標記的長,這樣當一個請求去操作數(shù)據(jù)庫的時候,其他請求拿的是上一次緩存數(shù)據(jù)
<?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);
//此處應(yīng)該進行數(shù)據(jù)庫查詢...
//$result = 數(shù)據(jù)庫查詢結(jié)果, 并且設(shè)置的時間要比cash_key長,這里設(shè)置為2倍;
$this->redis->set($this->redis_key, $result, $this->expire * 2);
}
return $result;
}
}