Redis分布式鎖防止緩存擊穿的實(shí)現(xiàn)
緩存擊穿
和緩存穿透不同的是,緩存擊穿是指:緩存中沒(méi)有,但是數(shù)據(jù)庫(kù)中存在的熱點(diǎn)數(shù)據(jù)。
例如:首頁(yè)的熱點(diǎn)新聞,并發(fā)訪(fǎng)問(wèn)量非常大的熱點(diǎn)數(shù)據(jù),如果緩存過(guò)期失效,服務(wù)器會(huì)去查詢(xún)DB,這時(shí)候如果大量的并發(fā)去查詢(xún)DB,可能會(huì)瞬間壓垮DB。
畫(huà)了個(gè)簡(jiǎn)圖,如下所示:

解決方案:DB查詢(xún)加分布式鎖。
未加鎖的情況
解決問(wèn)題之前,先看一下不做處理的代碼和運(yùn)行情況。
根據(jù)商品ID查詢(xún)商品詳情代碼

清空Redis緩存,開(kāi)啟5個(gè)線(xiàn)程去并發(fā)訪(fǎng)問(wèn)測(cè)試,測(cè)試代碼如下:

我們預(yù)期希望DB只查詢(xún)一次,后面4個(gè)查詢(xún)從Redis緩存中取就行,但是結(jié)果:
沒(méi)有加分布式鎖,結(jié)果也在意料之中,但是這樣容器給DB造成很大壓力。
如果是單臺(tái)服務(wù)器,直接使用Java的同步鎖即可

遺憾的是,通常后端是會(huì)部署集群的,Java的同步鎖可沒(méi)辦法實(shí)現(xiàn)分布式鎖。
Redis分布式鎖解決緩存擊穿
Java的內(nèi)置鎖只能應(yīng)用在單臺(tái)機(jī)器上,無(wú)法實(shí)現(xiàn)分布式,可以巧用Redis來(lái)實(shí)現(xiàn)分布式鎖。
加了分布式鎖后的代碼
//根據(jù)ID查詢(xún)商品
@GetMapping("/{id}")
public R id(@PathVariable String id){
//先查Redis緩存
Object o = redisTemplate.opsForValue().get(id);
if (o != null) {
//命中緩存
System.err.println("id:"+id+",命中redis緩存...");
return R.success(o);
}
//緩存未命中 查詢(xún)數(shù)據(jù)庫(kù)
String lockKey = "lock" + id;
//加鎖,10s后過(guò)期
for (;;) {
if (redisTemplate.opsForValue().setIfAbsent(lockKey, System.currentTimeMillis(), 10L, TimeUnit.SECONDS)) {
//加鎖成功的線(xiàn)程,再次檢查
o = redisTemplate.opsForValue().get(id);
if (o != null) {
//命中緩存
System.err.println("Thread:" + Thread.currentThread().getName() + ",id:"+id+",命中redis緩存...");
//釋放鎖
redisTemplate.delete(lockKey);
return R.success(o);
}
//仍未命中
System.err.println("Thread:" + Thread.currentThread().getName() + ",id:" + id + ",查詢(xún)DB...");
Goods goods = goodsMapper.selectById(id);
//結(jié)果存入Redis
redisTemplate.opsForValue().set(id, goods);
//釋放鎖
redisTemplate.delete(lockKey);
return R.success(goods);
}
//競(jìng)爭(zhēng)不到鎖,暫時(shí)讓出CPU資源
Thread.yield();
}
}
啟動(dòng)5個(gè)線(xiàn)程,并發(fā)訪(fǎng)問(wèn),結(jié)果如下圖:

這里介紹的只是最簡(jiǎn)單的方案,實(shí)際情況要考慮復(fù)雜的多,例如:不能誤解鎖、鎖超時(shí)等問(wèn)題。
到此這篇關(guān)于Redis分布式鎖防止緩存擊穿的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Redis分布式鎖防止緩存擊穿內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis鏈表底層實(shí)現(xiàn)及生產(chǎn)實(shí)戰(zhàn)
Redis 的 List 是一個(gè)雙向鏈表,鏈表中的每個(gè)節(jié)點(diǎn)都包含了一個(gè)字符串。是redis中最常用的數(shù)據(jù)結(jié)構(gòu)之一,本文主要介紹了Redis鏈表底層實(shí)現(xiàn)及生產(chǎn)實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
Centos7.3安裝Redis4.0.6詳細(xì)圖文教程
這篇文章主要介紹了Centos7.3安裝Redis4.0.6詳細(xì)教程圖解,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10
redis數(shù)據(jù)結(jié)構(gòu)之intset的實(shí)例詳解
這篇文章主要介紹了redis數(shù)據(jù)結(jié)構(gòu)之intset的實(shí)例詳解的相關(guān)資料, intset也即整數(shù)集合,當(dāng)集合保存的值數(shù)量不多時(shí),redis使用intset作為其底層數(shù)據(jù)保存結(jié)構(gòu),希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09
redis哨兵模式分布式鎖實(shí)現(xiàn)與實(shí)踐方式(redisson)
這篇文章主要介紹了redis哨兵模式分布式鎖實(shí)現(xiàn)與實(shí)踐方式(redisson),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
redis慢查詢(xún)?nèi)罩镜脑L(fǎng)問(wèn)和管理方式
這篇文章主要介紹了redis慢查詢(xún)?nèi)罩镜脑L(fǎng)問(wèn)和管理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
redis主從切換導(dǎo)致的數(shù)據(jù)丟失與陷入只讀狀態(tài)故障解決方案
這篇文章主要介紹了redis主從切換導(dǎo)致的數(shù)據(jù)丟失與陷入只讀狀態(tài)故障解決方案的相關(guān)資料,需要的朋友可以參考下2023-05-05

