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ù)類(lèi)
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和zookeeper實(shí)現(xiàn)分布式鎖
這篇文章主要介紹了如何操作Redis和zookeeper實(shí)現(xiàn)分布式鎖的相關(guān)資料,需要的朋友可以參考下2017-07-07
redis分布式鎖之可重入鎖的實(shí)現(xiàn)代碼
Redis過(guò)期數(shù)據(jù)的刪除策略詳解
Redis實(shí)現(xiàn)分布式隊(duì)列淺析
使用寶塔在服務(wù)器上配置Redis的詳細(xì)圖文教程

