在分布式系統開發中,緩存與數據庫的一致性問題是后端開發面試的核心考點之一。本文結合 PmHub 項目實踐,整理高頻面試題及深度解答,幫助開發者系統掌握緩存一致性解決方案的設計與實現。若想對相關內容有更透徹的理解,強烈推薦參考之前發布的博文:【PmHub后端篇】PmHub 中緩存與數據庫一致性的實現方案及分析
1 項目中如何保證緩存和數據一致性
我們主要采用Cache Aside 模式,核心邏輯是:
- 讀流程:
- 先查詢緩存,若命中直接返回
- 若未命中,查詢數據庫
- 將查詢結果寫入緩存(設置合理過期時間)
- 寫流程:
- 先更新數據庫
- 刪除對應緩存(而非更新,原因見問題 2)
同時結合以下輔助策略:
- 分布式鎖:處理高并發寫沖突(如流程狀態更新場景,通過 Redisson 實現分布式鎖)
- 緩存監控:通過 SkyWalking 監控緩存命中率(目標 > 90%)、淘汰率(<5%)
- 定時任務:每日凌晨對冷數據進行全量緩存重建
2 為什么刪除緩存,而不更新緩存
主要基于以下考量:
- 更新緩存浪費服務器資源:頻繁的緩存更新可能導致緩存服務器的負載增加,通過刪除緩存而不是頻繁更新,可以減少緩存服務器的壓力,提高系統整體性能。
- 避免臟數據:高并發下更新緩存可能讀到中間態數據(如事務未提交)
- 減少無效更新:頻繁寫場景下,刪除緩存可避免緩存與 DB 的無效同步
3 先更新 DB,再刪除緩存是否是完美解決方案
- 局限性分析:
-
寫性能損耗:每次寫操作需額外執行緩存刪除(優化方向:通過異步隊列批量處理緩存失效)
-
極端并發不一致:
場景:線程 A 更新 DB 后未刪緩存時,線程 B 讀取到舊緩存
概率:僅發生在 DB 更新成功但緩存刪除失敗,且此時有讀請求的極端情況
解決方案:- 緩存設置短過期時間(如 5 分鐘)
- 增加異步校驗任務(定時對比 DB 與緩存數據)
-
緩存穿透與預熱問題:
- 冷數據首次訪問會穿透到 DB(布隆過濾器攔截無效請求)
- 系統冷啟動時需預熱熱點數據(啟動時通過 Spring 事件監聽異步加載)
-
4 Cache Aside 模式有哪些局限性?
- 緩存穿透:首次訪問冷數據時存在 DB 穿透風險(解決方案:布隆過濾器攔截無效 key)
- 并發不一致:極端情況下(如更新 DB 后刪除緩存前服務宕機)可能不一致(解決方案:分布式鎖 + 異步數據校驗任務)
- 緩存預熱:冷啟動時需要預熱熱點數據(解決方案:啟動時通過 @PostConstruct 預熱或異步線程加載)
- 寫性能影響:每次寫操作需額外執行緩存刪除(優化方向:合并批量寫操作,使用 Redis Pipeline 批量刪除)
5 Spring Cache 如何集成 Redis?
Spring Cache 默認使用本地緩存(ConcurrentMap),集成 Redis 需:
- 添加 Redis 緩存依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置 CacheManager:
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisConnectionFactory connectionFactory) {return builder -> builder.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig(connectionFactory)).withCacheConfiguration("yourCacheName", RedisCacheConfiguration.defaultCacheConfig(connectionFactory).entryTtl(Duration.ofMinutes(5)));
}
- 使用
@Cacheable/@CacheEvict
注解聲明緩存操作。
6 如果緩存刪除失敗怎么辦?
采用重試機制 + 消息隊列:
- 刪除緩存失敗時,將 key 寫入 RabbitMQ 死信隊列
- 通過消費者異步重試刪除(設置 3 次重試間隔,避免洪峰)
7 如何處理緩存雪崩?
- 緩存層加隨機過期時間(如 10-15 分鐘隨機)
- 對熱點數據加本地緩存(Caffeine)作為保護罩
- 服務層添加 Hystrix 熔斷,防止 DB 被壓垮
6 參考鏈接
PmHub如何保證緩存和數據庫的一致性