先说结论
常见方案是 Cache Aside:读请求先查缓存,未命中再查数据库并回填;写请求先更新数据库,再删除缓存。它通常保证最终一致性,而不是严格强一致。
为什么是删除而不是更新缓存?
更新缓存可能依赖多张表计算,也可能被低频读取造成无效写入。删除缓存让下一次读取根据最新数据库状态重新构建,逻辑更简单。
并发窗口
即使先更新数据库再删除缓存,也可能出现旧读请求在删除之后把旧值重新写回缓存。实际系统会根据一致性等级选择过期时间、延迟二次删除、互斥重建或基于消息的失效通知。
消息与 Binlog 方案
写入数据库后发送消息删除缓存,需要处理消息发送失败和重复消费。更可靠的做法是使用事务消息、Outbox,或者订阅数据库 Binlog,把数据变更转成缓存失效事件。
如何选择方案?
- 商品详情等读多写少场景:Cache Aside + 合理 TTL。
- 库存、余额等强约束数据:数据库或专门一致性服务作为判断依据,不能只信缓存。
- 大规模派生缓存:CDC/Binlog + 消息队列异步失效。
- 热点 key:增加互斥重建、逻辑过期和限流,防止击穿。
怎么讲更清楚
先说明业务需要的是强一致还是最终一致,再给出读写流程、失败补偿、监控指标和降级方案。脱离业务直接说“延迟双删就能保证一致”是不严谨的。
并发时序为什么重要
先删缓存再写数据库时,另一个线程可能在写库前读到旧值并回填。先写数据库再删缓存仍有极小窗口,但发生条件更苛刻,也更容易通过 TTL 和可靠删除补偿。延迟双删可以降低部分并发回填风险,但等待时间难以精确设置,不能代替可靠重试。
常见问题
追问 1:为什么删除缓存而不是更新缓存?
删除是幂等操作,容易重试;直接更新还要处理并发写入顺序,并可能为不再读取的数据做无效维护。
追问 2:如何做到真正强一致?
旁路缓存天然适合最终一致。关键读可绕过缓存,或使用支持事务的一致性存储,但必须接受更高延迟或更低可用性。
追问 3:分布式锁能解决全部一致性问题吗?
不能。它能限制并发回源,却无法原子覆盖数据库提交与缓存删除,还要处理锁超时、进程暂停和故障切换。