一 緩存穿透
緩存穿透指的是,當我們訪問某個緩存KEY想取得對應的數據時,若此KEY不存在於緩存中,則會去查庫。如何解決呢?將每次查詢的結果都放入緩存不管是不是空。
public function getArticles($key)
{
$expire = 60 * 3;
$data = Cache::get($key);
//注意:此處使用is_null來判斷而不是直接使用 (!$data)來判斷。
//使用 (!$data)來判斷的弊端是:如果$data的值爲空字符串或者空數組,此處也是不成立的,會繼續執行查詢DB的語句,造成緩存穿透
if (!is_null($data)) {
return $data;
}
$data = $this->searchDB();
Cache::put($key, $data, $expire);
return $data;
}
這樣處理的原因是,即使當前查詢的key爲空字符串,或者空數組,結果也會被緩存起來。當下一次訪問時會直接返回,不會造成緩存穿透
二 緩存加鎖(Redis)
若系統的併發很高,當緩存過期時,則大量的請求會穿透緩存,同時到DB中查詢,那我們可以設置緩存當緩存過期時,只去DB中請求一次並緩存嗎?可以,我們可以使用redis的setNx()
setNx($key) 的作用類似於set($key) ,setNx的意思爲 set Not Exists 如果$key不存在則設置,存在則不進行任何操作. 設置成功設置返回1,說明當前的請求獲得了當前的操作權限,設置失敗返回0,說明此資源已經被其他請求獲得。
使用代碼實現的話,思路如下:
- 給存入緩存的數據增加一個過期時間字段暫時給這個字段起名字叫$data['expire'](這個過期時間要短於實際的緩存過期時間),方便在緩存過期前執行加鎖和緩存更新。
- 如果$data['expire']達到過期時間,則執行加鎖以及緩存更新。
- 此時如果有其他請求進入則返回更新之前的數據。
代碼如下:
public function getArticlesLock($key)
{
$time = time();
$expire = 10 * 2;
$lockKey = 'lock:k';
$data = Cache::get($key);
if (!is_null($data)) {
//緩存未過期
if ($data['expire'] > time()){
return $data['data'];
}
//加鎖失敗說明已經有請求執行加鎖,返回之前的緩存數據
if (!Redis::setnx($lockKey,1)) {
return $data['data'];
}
}
sleep(3);
$datat = $this->searchDB();
$data = [
'data' => $datat,
'expire' => $time + $expire - 10
];
$r = Cache::put($key, $data, $expire);
//解鎖
Redis::del($lockKey);
return $data['data'];
}
當然此處也可以使用set()
來代替setnx()
加鎖,以及使用lua腳本解鎖。
以上內容希望幫助到大家,更多PHP大廠PDF面試文檔,PHP進階架構視頻資料,PHP精彩好文免費獲取可以微信搜索關注公衆號:PHP開源社區,或者訪問: