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

本文經過閱讀 原文 此文進行整理,看原文的同窗們請移步至此

一 緩存穿透

緩存穿透指的是,當咱們訪問某個緩存KEY想取得對應的數據時,若此KEY不存在於緩存中,則會去查庫。如何解決呢?將每次查詢的結果都放入緩存無論是否是空。redis

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腳本解鎖。this

相關文章
相關標籤/搜索