0、熱點數據集中失效問題redis
說明:對於一些熱點的數據來講,當緩存失效之後會存在大量的請求過來,而後打到數據庫去,從而可能致使數據庫崩潰的狀況。數據庫
解決方案:一、設置不一樣緩存時間,好比在一個基礎的時間上加上或者減去一個範圍內的隨機值。緩存
二、第一個請求去查詢數據庫的時候對他加一個互斥鎖,其他的查詢請求都會被阻塞住,直到鎖被釋放,從而保護數據庫。可是也是因爲它會阻塞其餘的線程,此時系統吞吐量會降低。安全
一、緩存穿透併發
說明:緩存穿透是指查詢一個必定不存在的數據,因爲緩存是不命中時被動寫的,而且出於容錯考慮,若是從存儲層查不到數據則不寫入緩存,這將致使這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了,要是有人利用不存在的key頻繁攻擊咱們的應用,這就是漏洞。高併發
解決方案:有不少種方法能夠有效地解決緩存穿透問題工具
一、最多見的則是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。spa
二、另外也有一個更爲簡單粗暴的方法(咱們採用的就是這種),若是一個查詢返回的數據爲空(不論是數 據不存在,仍是系統故障),咱們仍然把這個空結果進行緩存,但它的過時時間會很短,最長不超過五分鐘。線程
二、緩存雪崩設計
說明:緩存雪崩是指在咱們設置緩存時採用了相同的過時時間,致使緩存在某一時刻同時失效,請求所有轉發到DB,DB瞬時壓力太重雪崩。
解決方案:一、緩存失效時的雪崩效應對底層系統的衝擊很是可怕。大多數系統設計者考慮用加鎖或者隊列的方式保證緩存的單線 程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。這裏分享一個簡單方案就時講緩存失效時間分散開,好比咱們能夠在原有的失效時間基礎上增長一個隨機值,好比1-5分鐘隨機,這樣每個緩存的過時時間的重複率就會下降,就很難引起集體失效的事件。
二、事前:這種方案就是在發生雪崩前對緩存集羣實現高可用,若是是使用 Redis,可使用 主從+哨兵 ,Redis Cluster 來避免 Redis 全盤崩潰的狀況。
三、事中:使用 Hystrix進行限流 & 降級 ,好比一秒來了5000個請求,咱們能夠設置假設只能有一秒 2000個請求能經過這個組件,那麼其餘剩餘的 3000 請求就會走限流邏輯。而後去調用咱們本身開發的降級組件(降級),好比設置的一些默認值呀之類的。以此來保護最後的 MySQL 不會被大量的請求給打死。
四、過後:開啓Redis持久化機制,儘快恢復緩存集羣
三、緩存擊穿
說明:對於一些設置了過時時間的key,若是這些key可能會在某些時間點被超高併發地訪問,是一種很是「熱點」的數據。這個時候,須要考慮一個問題:緩存被「擊穿」的問題,這個和緩存雪崩的區別在於這裏針對某一key緩存,前者則是不少key。
解決方案:一、使用互斥鎖:業界比較經常使用的作法,是使用mutex。簡單地來講,就是在緩存失效的時候(判斷拿出來的值爲空),不是當即去load db,而是先使用緩存工具的某些帶成功操做返回值的操做(好比Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操做返回成功時,再進行load db的操做並回設緩存;不然,就重試整個get緩存的方法。
來個示例代碼
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; } }
對比其他解決方案
總結
針對業務系統,永遠都是具體狀況具體分析,沒有最好,只有最合適。
最後,對於緩存系統常見的緩存滿了和數據丟失問題,須要根據具體業務分析,一般咱們採用LRU策略處理溢出,Redis的RDB和AOF持久化策略來保證必定狀況下的數據安全。