PHP 緩存穿透/使用Redis進行緩存加鎖

一 緩存穿透

緩存穿透指的是,當我們訪問某個緩存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,說明此資源已經被其他請求獲得。使用代碼實現的話,思路如下:

  1. 給存入緩存的數據增加一個過期時間字段暫時給這個字段起名字叫$data['expire'](這個過期時間要短於實際的緩存過期時間),方便在緩存過期前執行加鎖和緩存更新。
  2. 如果$data['expire']達到過期時間,則執行加鎖以及緩存更新。
  3. 此時如果有其他請求進入則返回更新之前的數據。

代碼如下:

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開源社區,或者訪問:

2021金三銀四大廠面試真題集錦,必看!

四年精華PHP技術文章整理合集——PHP框架篇

四年精華PHP技術文合集——微服務架構篇

四年精華PHP技術文合集——分佈式架構篇

四年精華PHP技術文合集——高併發場景篇

四年精華PHP技術文章整理合集——數據庫篇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章