双写一致性 (Dual-Write Consistency) 探讨当数据同时存在于数据库 (如 MySQL) 和缓存 (如 Redis) 时,如何保证两者的数据同步。
核心挑战:为什么难以保持一致?
在更新数据时,数据库和缓存的操作不是原子的。无论先操作谁,只要第二步失败,就会导致数据不一致:
| 痛点场景 | 根本原因 | 最终后果 |
|---|---|---|
| 操作异常 | 第二步(如删缓存)失败,主流程已返回 | 缓存中永远是旧数据(脏数据) |
| 并发冲突 | 多个请求交错执行,后发的请求覆盖了先发的更新 | 数据库与缓存数据版本不匹配 |
| 主从延迟 | 缓存刚删完,读请求从从库读到旧数据并回填 | 缓存被“脏数据”瞬间回填 |
解决方案概览
针对上述挑战,业界演进出了以下几种主流对策:
| 方案名称 | 核心逻辑 | 解决的痛点 | 一致性级别 | 适用场景 |
|---|---|---|---|---|
| 旁路缓存 (Cache Aside) | 先更 DB,再删 Cache | 降低冲突概率 | 弱一致性 | 大多数互联网业务 (推荐) |
| 延迟双删 | 更 → 删 → 延迟再删 | 主从延迟 | 最终一致性 | 主从延迟严重的架构 |
| 版本号/时间戳校验 | 写入带版本,读取校验版本 | 乱序写入 | 最终一致性 | 高并发、允许短暂不一致 |
| 异步补偿 | 监听变更,重试删除 | 操作失败 | 最终一致性 (高可靠) | 高可靠、解耦需求 |
| 分布式读写锁 | 强行串行化读写 | 并发冲突 | 强一致性 | 金融等强一致场景 |
总结建议
-
追求性能与最终一致性 (推荐):采用“先更 DB,再删缓存”模式。配合 TTL 过期机制 作为兜底,高可靠场景下引入 MQ 或 Binlog 异步重试。
-
追求强一致性:使用读写锁或直接查询数据库。
-
追求性能与最终一致性 (推荐):采用“先更 DB,再删缓存”模式。配合 TTL 过期机制 作为兜底,高可靠场景下引入 MQ 或 Binlog 异步重试。
-
追求强一致性:使用读写锁或直接查询数据库。