擊穿:指的是單一key在快取中查不到,去資料庫查詢,這樣如果資料量不大或並發不大的話是沒有什麼問題的。
如果資料庫資料量大且是高並發的情況下那麼就可能會造成資料庫壓力過大而崩潰
注意:這裡指的是單一key發生高併發!!! (建議學習:Redis視訊教學)
解決方案:
#1) 透過synchronized 雙重檢查機制:某個key只讓一個執行緒查詢,阻塞其它執行緒
在同步區塊中,繼續判斷檢查,保證不存在,才去查DB。
例如:
private static volaite Object lockHelp=new Object(); public String getValue(String key){ String value=redis.get(key,String.class); if(value=="null"||value==null||StringUtils.isBlank(value){ synchronized(lockHelp){ value=redis.get(key,String.class); if(value=="null"||value==null||StringUtils.isBlank(value){ value=db.query(key); redis.set(key,value,1000); } } } return value; }
#缺點: 會阻塞其它執行緒
2)設定value永不過期
這種方式可以說是最可靠的,最安全的但是佔空間,記憶體消耗大,並且不能保持資料最新這個需要根據具體的業務邏輯來做
個人覺得如果要保持資料最新不放這麼試試,僅供參考:
起個計時任務或利用TimerTask做定時,每個一段時間多這些值進行資料庫查詢更新一次緩存,當然前提時不會給資料庫造成壓力過大(這個很重要)
3) 使用互斥鎖(mutex key)
業界比較常用的做法,是使用mutex。簡單來說,就是在快取失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用快取工具的某些帶成功操作回傳值的操作(例如Redis的SETNX或Memcache的ADD)去set一個mutex key,當操作回傳成功時,再進行load db的操作並回設快取;否則,就重試整個get快取的方法。
SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才會設定,可以利用它來實現鎖定的效果。在redis2.6.1之前版本未實現setnx的過期時間,所以這裡給出兩種版本代碼參考:
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); return value; } else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可 sleep(10); get(key); //重试 } } else { return value; } }
更多Redis相關技術文章,請訪問Redis資料庫使用入門教程欄位進行學習!
以上是redis擊穿怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!