1. Redis緩存雪崩redis
發生場景數據庫
當Redis服務器重啓或者大量緩存在同一時期失效時,此時大量的流量會所有衝擊到數據庫上面,數據庫有可能會由於承受不住而宕機;
因此此時的緩存層出現了錯誤,因而全部的請求都會到達存儲層,存儲層的調用量會暴漲,形成存儲層也完蛋了的狀況;緩存
解決方案tomcat
- 均勻分佈 : 咱們應該在設置失效時間時應該儘可能均勻的分佈,好比失效時間是當前時間加上一個時間段的隨機值,讓失效的時間保持隨機性,不在同一時間點失效。
- 熔斷機制 : 相似於SpringCloud的熔斷器,咱們能夠設定閾值或監控服務,若是達到熔斷閾值(QPS,服務沒法響應,服務超時)時,則直接返回,再也不調用目標服務,而且還須要一個檢測機制,若是目標服務已經能夠正常使用,則重置閾值,恢復使用。
- 隔離機制 : 相似於Docker同樣,當一個服務器上某一個tomcat出了問題後不會影響到其它的tomcat,這裏咱們可使用線程池來達到隔離的目的,當線程池執行拒絕策略後則直接返回,再也不向線程池中增長任務。
- 限流/降流機制 : 其實限流就是熔斷機制的一個版本,設置閾值(QPS),達到閾值以後直接返回。
- 雙緩存機制 : 將數據存儲到緩存中時存儲倆份,一份的有效期是正常的,一份的有效期長一點.不建議用這個方案,由於比較消耗內存資源,畢竟Redis是直接存儲到內存中的。
- **集羣/redis高可用:**既然一臺redis扛不住,那就多弄幾臺,這臺掛掉還有其它的能夠繼續工做,也就是搭建的一個集羣。
2. Redis緩存穿透服務器
發生場景併發
當用戶想查詢某條數據,發現redis數據庫沒有,即緩存沒有命中;繼續向持久層數據庫查詢,仍是沒有,即本次查詢失敗;當不少次用戶查詢都失敗,因而請求都去請求 了持久層;此時持久層的數據庫就會產生很大很大壓力,因而就出現了緩存穿透。ide
解決方案高併發
布隆過濾 : 咱們能夠預先將數據庫裏面全部的key所有存到一個大的map裏面,而後在過濾器中過濾掉那些不存在的key.可是須要考慮數據庫的key是會更新的,此時須要考慮數據庫 --> map的更新頻率問題
緩存空值 : 哪怕這條數據不存在可是咱們任然將其存儲到緩存中去,設置一個較短的過時時間便可,而且能夠作日誌記錄,尋找問題緣由工具
3. Redis緩存擊穿url
緩存擊穿,是指一個key很是熱點,在不停的扛着大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破緩存,直接請求數據庫,就像在一個屏障上鑿開了一個洞。
好比當前的商品降價,促銷;或者微博熱搜不少人同時訪問,那麼這個Key就是一個熱點,同時扛着超高併發,集中訪問,緩存就被擊穿。
解決方案
使用互斥鎖(mutex key)
業界比較經常使用的作法,是使用mutex。簡單地來講,就是在緩存失效的時候(判斷拿出來的值爲空),不是當即去load db,而是先使用緩存工具的某些帶成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操做返回成功時,再進行load db的操做並回設緩存;不然,就重試整個get緩存的方法。
SETNX,是「SET if Not eXists」的縮寫,也就是隻有不存在的時候才設置,能夠利用它來實現鎖的效果。在redis2.6.1以前版本未實現setnx的過時時間,因此這裏給出兩種版本代碼參考:
//2.6.1前單機版本鎖 String get(String key) { String value = redis.get(key); if (value == null) { if (redis.setnx(key_mutex, "1")) { // 3 min timeout to avoid mutex holder crash redis.expire(key_mutex, 3 * 60) value = db.get(key); redis.set(key, value); redis.delete(key_mutex); } else { //其餘線程休息50毫秒後重試 Thread.sleep(50); get(key); } } } 最新版本代碼: public String get(key) { String value = redis.get(key); if (value == null) { //表明緩存值過時 //設置3min的超時,防止del操做失敗的時候,下次緩存過時一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //表明設置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { //這個時候表明同時候的其餘線程已經load db並回設到緩存了,這時候重試獲取緩存值便可 sleep(50); get(key); //重試 } } else { return value; } } mem
文獻參考:
https://baijiahao.baidu.com/sid=1655304940308056733&wfr=spider&for=pc
https://blog.csdn.net/zeb_perfect/article/details/54135506