Skip to content

Redis 缓存与数据库一致性

一句话机制:缓存与 DB 双写无法做到强一致,只能追求最终一致。业界最常用方案是「更新 DB → 删缓存」,并叠加「延时双删」与失败补偿来收窄不一致窗口。

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

  • 三种经典方案:
    1. 先更新 DB 再更新缓存:不推荐(线程安全难保证;写多读少时缓存更新纯浪费)。
    2. 先删缓存再更新 DB:存在不一致窗口(删缓存→B 查旧值写缓存→A 写 DB),用延时双删(删→写 DB→sleep→再删)缓解。
    3. 先更新 DB 再删缓存(Cache-Aside 推荐):正常情况下读请求会回填最新值;仍有极小窗口(删缓存失败/并发回源旧值)。
  • 没有银弹:所有方案都只能在「性能 vs 一致性」间取舍,最终一致是现实目标。
  • 推荐组合:更新 DB + 删缓存 + 延时双删;删缓存失败用消息队列/重试补偿。

关键证据 / 例子

  • 「先删缓存再更新 DB」的脏数据场景:A 删缓存→B 查缓存未命中→B 读 DB 旧值→B 写旧值进缓存→A 写新值进 DB → 缓存脏。
  • 延时双删伪代码:del(key)db.update()sleep(1000)del(key);第二次删除可异步化以保吞吐。
  • 即便「先更新 DB 再删缓存」,若删缓存失败仍不一致 → 需 binlog 监听(Canal)或 MQ 重试兜底。

常见误解

  • ❌「加缓存一定能保证和 DB 一致」——缓存本质是副本,双写必有窗口;强一致需放弃缓存或用分布式事务(不现实)。
  • ❌「先删缓存就够了」——并发下极易产生脏数据,必须配合双删或「更新 DB 再删」。
  • ❌「删缓存失败无所谓」——失败即长期脏数据,必须有补偿(重试/MQ/canal)。
  • ❌「Cache-Aside 先更新 DB 再删缓存就完美」——仍有极短不一致窗口(回源与删除竞态),靠 TTL 兜底最终一致。

关联

最近更新