本文經過閱讀 原文 此文進行整理,看原文的同窗們請移步至此
緩存穿透指的是,當咱們訪問某個緩存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爲空字符串,或者空數組,結果也會被緩存起來。當下一次訪問時會直接返回,不會形成緩存穿透數組
若系統的併發很高,當緩存過時時,則大量的請求會穿透緩存,同時到DB中查詢,那咱們能夠設置緩存當緩存過時時,只去DB中請求一次並緩存嗎?能夠,咱們可使用redis的setNx()setNx($key) 的做用相似於set($key) ,setNx的意思爲 set Not Exists 若是$key不存在則設置,存在則不進行任何操做. 設置成功設置返回1,說明當前的請求得到了當前的操做權限,設置失敗返回0,說明此資源已經被其餘請求得到。
使用代碼實現的話,思路以下:緩存
代碼以下:併發
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