redis擊穿現(xiàn)象如何防止
背景
大家都知道,計算機的瓶頸之一就是IO,為了解決內(nèi)存與磁盤速度不匹配的問題,產(chǎn)生了緩存,將一些熱點數(shù)據(jù)放在內(nèi)存中,隨用隨取,降低連接到數(shù)據(jù)庫的請求鏈接,避免數(shù)據(jù)庫掛掉。需要注意的是,無論是擊穿還是后面談到的穿透與雪崩,都是在高并發(fā)前提下 ,當(dāng)緩存中某一個熱點key失效。
擊穿
指的是單個key在緩存中查不到,去數(shù)據(jù)庫查詢,這樣如果數(shù)據(jù)量不大或者并發(fā)不大的話是沒有什么問題的。
如果數(shù)據(jù)庫數(shù)據(jù)量大并且是高并發(fā)的情況下那么就可能會造成數(shù)據(jù)庫壓力過大而崩潰
注意: 這里指的是單個key發(fā)生高并發(fā)!!!
案例
這里要注意的是這是某一個熱點key過期失效和后面介紹緩存雪崩是有區(qū)別的。比如淘寶雙十一,對于某個特價熱門的商品信息,緩存在Redis中,剛好0點,這個商品信息在Redis中過期查不到了,這時候大量的用戶又同時正好訪問這個商品,就會造成大量的請求同時到達數(shù)據(jù)庫。
解決方案
通過synchronized+雙重檢查機制
某個key只讓一個線程查詢,阻塞其它線程
在同步塊中,繼續(xù)判斷檢查,保證不存在,才去查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; }
缺點: 會阻塞其它線程
設(shè)置value永不過期(設(shè)置熱點數(shù)據(jù)永不過期)
這種方式可以說是最可靠的,最安全的但是占空間,內(nèi)存消耗大,并且不能保持數(shù)據(jù)最新 這個需要根據(jù)具體的業(yè)務(wù)邏輯來做
個人覺得如果要保持數(shù)據(jù)最新不放這么試試,僅供參考:
起個定時任務(wù)或者利用TimerTask 做定時,每個一段時間多這些值進行數(shù)據(jù)庫查詢更新一次緩存,當(dāng)然前提時不會給數(shù)據(jù)庫造成壓力過大(這個很重要)
使用互斥鎖(mutex key)
業(yè)界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當(dāng)操作返回成功時,再進行l(wèi)oad db的操作并回設(shè)緩存;否則,就重試整個get緩存的方法。
SETNX,是「SET if Not eXists」的縮寫,也就是只有不存在的時候才設(shè)置,可以利用它來實現(xiàn)鎖的效果。在redis2.6.1之前版本未實現(xiàn)setnx的過期時間,所以這里給出兩種版本代碼參考:
public String get(key) { String value = redis.get(key); if (value == null) { //代表緩存值過期 //設(shè)置3min的超時,防止del操作失敗的時候,下次緩存過期一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表設(shè)置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); return value; } else { //這個時候代表同時候的其他線程已經(jīng)load db并回設(shè)到緩存了,這時候重試獲取緩存值即可 sleep(10); get(key); //重試 } } else { return value; } }
缺點:
- 代碼復(fù)雜度增大
- 存在死鎖的風(fēng)險
- 存在線程池阻塞的風(fēng)險
擊穿與雪崩的不同在于緩存key失效的量級上。擊穿是對于單個key值的緩存失效過期,雪崩則是大面積key同時失效。
到此這篇關(guān)于redis擊穿現(xiàn)象如何防止的文章就介紹到這了,更多相關(guān)redis 擊穿內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis數(shù)據(jù)結(jié)構(gòu)類型示例解析
這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)類型示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Redis整合SpringBoot的RedisTemplate實現(xiàn)類(實例詳解)
這篇文章主要介紹了Redis整合SpringBoot的RedisTemplate實現(xiàn)類,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01