双写一致性本质是降低不一致概率并确保最终一致,核心是以数据库为唯一数据源、缓存仅作加速层;推荐主方案为先更新数据库再删除缓存(Cache Aside),辅以重试、延迟双删或Binlog+MQ异步同步等兜底策略。

Redis 和数据库(比如 MySQL)双写一致性,本质不是“完全避免不一致”,而是用合理策略把不一致的概率压到最低、时间缩到最短,并确保最终能回到一致状态。核心思路是:以数据库为唯一数据源,缓存只作加速层;写操作优先保数据库正确,缓存操作可失败、可重试、可延迟。
推荐主方案:先更新数据库,再删除缓存(Cache Aside)
这是目前最常用、风险相对可控的实践方式。
- 为什么不是“更新缓存”? 更新缓存容易引发并发脏写(比如 A、B 两个线程先后更新数据库,但 B 先写完缓存,A 后写覆盖,结果缓存反而比数据库旧);而删除缓存简单、幂等、无计算开销。
- 为什么先改库再删缓存? 如果删缓存失败,最多是下次读时多一次数据库查询;但如果先删缓存、再改库失败,就会导致缓存为空、数据库仍是旧值——用户读到的是“空”或“旧值”,但至少不会读到错误的新旧混合数据。
- 删缓存失败怎么办? 加重试机制(如最多 3 次)、记录日志、配合告警;也可异步发消息让下游补偿删除。
高并发下兜底:延时双删
单删不能完全防住“删缓存 → 查库回填旧值 → 改库完成”这类并发窗口,所以加第二次删除。
- 流程:删缓存 → 更新数据库 → 等待(如 100–500ms)→ 再删一次缓存。
- 等待时间目的:覆盖主从同步延迟(尤其读写分离架构下,读请求可能打到还没同步完的从库,回填的是旧值)。
- 注意:延时不等于绝对安全,它只是大幅降低概率;实际延时需结合你的主从同步监控数据来调优,不能拍脑袋定。
更可靠方案:消息队列异步解耦
把数据库变更当作事件发布出去,由独立消费者负责刷新/删除缓存,实现解耦和最终一致性。
- 例如:MySQL binlog + Canal / Debezium 抽取变更 → 发 Kafka → 消费者解析后执行 redis.del(key)。
- 优势:写数据库成功即算成功;缓存操作失败可重试、可追溯、不影响主链路性能。
- 适用场景:对强实时性要求不高(秒级一致即可),但对可靠性、可运维性要求高的系统。
极端一致性要求:读写串行化 or 分布式锁
当某条关键数据(如账户余额)绝对不能容忍任何中间态不一致时,可牺牲部分性能换确定性。
- 对同一 key 的读写请求,通过 Redis 分布式锁(如 SET key value EX 10 NX)强制串行处理。
- 或按 key 哈希分片,每个分片绑定单一工作线程,保证该 key 的所有操作顺序执行。
- 代价明显:吞吐下降、复杂度上升,仅建议用于极少数核心业务字段。


















