Skip to content

概念卡片:缓存一致性

一句话机制:缓存一致性解决「缓存和 DB 数据不同步」——核心是写策略:主流 Cache-Aside(旁路缓存) 用「先更新 DB、再删缓存」保证最终一致;高并发下用双删 + 延迟队列版本号CDC 兜住「删缓存前的并发读」窗口。

三种缓存模式

模式写流程特点
Cache-Aside(旁路)更新 DB → 删缓存主流,读未命中才加载缓存
Write-Through(写穿透)更新缓存 → 缓存同步写 DB缓存层负责持久化,读不穿透
Write-Behind(写回)更新缓存 → 异步落 DB写性能最高,宕机可能丢数据

关键代码示例(Cache-Aside)

java
// 读:未命中才加载
Data get(String key) {
    Data d = cache.get(key);
    if (d == null) { d = db.load(key); cache.put(key, d); }
    return d;
}
// 写:先更新 DB,再失效缓存(顺序不能反)
void update(String key, Data newData) {
    db.update(key, newData);
    cache.delete(key);
}

进阶一致性方案

方案解决什么
双删 + 延迟队列更新前先删一次,更新 DB 后再延迟删一次,兜住并发读旧值回填缓存的窗口
版本号 / 时间戳缓存带 version,更新时校验,旧版本不回填
CDC(订阅 binlog)订阅数据库变更日志,自动失效缓存,解耦业务代码

不变量(必须成立的约束)

  • 先更新 DB 再删缓存,顺序不能反——反过来会「删了缓存、DB 没更新、并发读又把旧值写回缓存」。
  • 双删的核心:第一次删「清旧值」,第二次延迟删「清并发读回填的旧值」,延迟时间略大于「读 DB + 回填」耗时。
  • 一致性目标是最终一致,不要追求强一致(除非用版本号 + 事务)。

踩坑案例

  • 现象:高并发下偶发缓存和 DB 不一致(缓存是旧值)。 原因:更新 DB 后、删缓存前,有并发读把旧值回填了缓存。解决:双删 + 延迟队列(或版本号 / CDC)。

常见误解

  • 以为「先删缓存再更新 DB」更安全 → 恰恰相反,会在删后、更新前被并发读回填旧值。
  • 以为缓存能强一致 → 缓存一致性本质是最终一致,强一致需版本号 + 事务,代价大。

关联

最近更新