主题
Redis 缓存与数据库一致性
一句话机制:缓存与 DB 双写无法做到强一致,只能追求最终一致。业界最常用方案是「更新 DB → 删缓存」,并叠加「延时双删」与失败补偿来收窄不一致窗口。
不变量(必须成立的约束)
- 三种经典方案:
- 先更新 DB 再更新缓存:不推荐(线程安全难保证;写多读少时缓存更新纯浪费)。
- 先删缓存再更新 DB:存在不一致窗口(删缓存→B 查旧值写缓存→A 写 DB),用延时双删(删→写 DB→sleep→再删)缓解。
- 先更新 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 兜底最终一致。
关联
- 来源:Redis常见面试题(面试-叶三方案对比最详尽)
- 相关:Redis技术栈总览、概念卡片:Redis缓存异常三件套-穿透击穿雪崩