Redis和數(shù)據(jù)庫(kù)雙寫(xiě)一致性問(wèn)題的解決方案
下面我將詳細(xì)解答Redis與數(shù)據(jù)庫(kù)雙寫(xiě)一致性問(wèn)題,結(jié)合故事舉例說(shuō)明,最后用思維導(dǎo)圖總結(jié)核心解決方案。
問(wèn)題解答
核心問(wèn)題:當(dāng)同時(shí)更新Redis緩存和數(shù)據(jù)庫(kù)時(shí),由于網(wǎng)絡(luò)延遲、操作順序等問(wèn)題導(dǎo)致數(shù)據(jù)不一致。
解決方案(含故事舉例)
假設(shè)電商平臺(tái)有商品庫(kù)存系統(tǒng):
// 商品服務(wù)類 public class ProductService { private DB db = new MySQL(); // 數(shù)據(jù)庫(kù) private Cache cache = new Redis(); // 緩存 }
1. 先更新數(shù)據(jù)庫(kù),再刪除緩存(推薦方案)
public void updateProduct(Product product) { db.update(product); // 1. 更新數(shù)據(jù)庫(kù) cache.delete(product.getId());// 2. 刪除緩存 }
故事場(chǎng)景:
管理員修改商品價(jià)格(100→80元):
- 數(shù)據(jù)庫(kù)先更新為80元 ?
- 刪除Redis中該商品的緩存
用戶查詢時(shí):
- 緩存未命中 → 讀數(shù)據(jù)庫(kù)(80元) ?
- 回填緩存(80元) ?
優(yōu)勢(shì):
- 避免"更新緩存失敗導(dǎo)致永久不一致"
- 緩存刪除失敗可重試(通過(guò)消息隊(duì)列)
風(fēng)險(xiǎn):
并發(fā)時(shí)短暫不一致(概率低):
- 用戶A讀緩存(空) → 讀數(shù)據(jù)庫(kù)(100元)
- 管理員更新數(shù)據(jù)庫(kù)(100→80) → 刪緩存
- 用戶A寫(xiě)緩存(100元) ?(舊數(shù)據(jù))
- 解決方案:延時(shí)雙刪(下文說(shuō)明)
2. 延時(shí)雙刪策略
public void updateWithDelay(Product product) { cache.delete(product.getId()); // 1. 先刪緩存 db.update(product); // 2. 更新數(shù)據(jù)庫(kù) Thread.sleep(500); // 3. 等待500ms cache.delete(product.getId()); // 4. 再刪緩存 }
故事場(chǎng)景:
解決上述并發(fā)問(wèn)題:
- 首次刪除:清空舊緩存
- 更新數(shù)據(jù)庫(kù) ?
- 等待期間可能寫(xiě)入的舊緩存被二次刪除 ?→?
3. 監(jiān)聽(tīng)數(shù)據(jù)庫(kù)變更(最終一致性)
// 使用Canal監(jiān)聽(tīng)MySQL binlog canal.subscribe(event -> { if (event.isUpdate()) { cache.delete(event.getKey()); // 異步刪除緩存 } });
故事場(chǎng)景:
訂單系統(tǒng)庫(kù)存變更:
- 數(shù)據(jù)庫(kù)減庫(kù)存 ?
- Canal捕獲變更事件
- 自動(dòng)刪除Redis庫(kù)存緩存
- 下次查詢回填最新值 ?
4. 加分布式鎖(強(qiáng)一致)
public void safeUpdate(Product product) { Lock lock = redisson.getLock("PRODUCT_" + product.getId()); lock.lock(); try { db.update(product); cache.update(product); // 同時(shí)更新緩存 } finally { lock.unlock(); } }
適用場(chǎng)景:
金融賬戶余額等強(qiáng)一致性要求:
- 讀寫(xiě)操作串行化
- 性能較低(非高并發(fā)場(chǎng)景)
總結(jié)對(duì)比
方案 | 一致性 | 性能 | 復(fù)雜度 | 適用場(chǎng)景 |
---|---|---|---|---|
先DB后刪緩存 | 最終 | ★★★★ | 低 | 大部分業(yè)務(wù) |
延時(shí)雙刪 | 最終 | ★★★ | 中 | 高并發(fā)場(chǎng)景 |
監(jiān)聽(tīng)binlog | 最終 | ★★★★ | 高 | 異構(gòu)系統(tǒng)同步 |
分布式鎖 | 強(qiáng)一致 | ★★ | 高 | 金融/賬戶系統(tǒng) |
核心原則:
- 優(yōu)先保證數(shù)據(jù)庫(kù)正確性
- 緩存操作可失敗/重試
- 強(qiáng)一致性需犧牲性能
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
redis用list做消息隊(duì)列的實(shí)現(xiàn)示例
本文主要介紹了redis用list做消息隊(duì)列的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02如何高效地向Redis插入大量的數(shù)據(jù)(推薦)
本篇文章主要介紹了如何高效地向Redis插入大量的數(shù)據(jù),現(xiàn)在分享給大家,感興趣的小伙伴們可以參考一下。2016-11-11解析Redis未授權(quán)訪問(wèn)漏洞復(fù)現(xiàn)與利用危害
這篇文章主要介紹了Redis未授權(quán)訪問(wèn)漏洞復(fù)現(xiàn)與利用,介紹了redis未授權(quán)訪問(wèn)漏洞的基本概念及漏洞的危害,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01Redis實(shí)現(xiàn)優(yōu)惠券限一單限制詳解
這篇文章主要介紹了Redis解決優(yōu)惠券秒殺應(yīng)用案例,本文先講了搶購(gòu)問(wèn)題,指出其中會(huì)出現(xiàn)的多線程問(wèn)題,提出解決方案采用悲觀鎖和樂(lè)觀鎖兩種方式進(jìn)行實(shí)現(xiàn),然后發(fā)現(xiàn)在搶購(gòu)過(guò)程中容易出現(xiàn)一人多單現(xiàn)象,需要的朋友可以參考下2022-12-12Redis集群增加節(jié)點(diǎn)與刪除節(jié)點(diǎn)的方法詳解
這篇文章主要給大家介紹了關(guān)于Redis集群增加節(jié)點(diǎn)與刪除節(jié)點(diǎn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09