真实面经题目 · 原创解析
缓存和数据库怎么保持数据一致性?还有别的做法吗?
这题考察 Cache Aside 的读写路径、并发竞态、删除失败补偿和一致性取舍,重点不是追求绝对一致,而是控制旧数据窗口。
缓存和数据库一致性通常先讲 Cache Aside:读请求先查缓存,未命中再查数据库并回填;写请求先更新数据库,再删除缓存,让下一次读重新加载新值。相比先删缓存再更新数据库,这个顺序更不容易让并发读把旧值写回缓存。但它仍然可能遇到删除缓存失败、主从延迟、读写并发和业务执行超时,所以要配 TTL、删除重试、延迟双删、消息队列、binlog 订阅补偿、版本号或分布式锁。最后要说明一致性级别:大多数缓存场景追求最终一致;强一致业务要减少旁路缓存,或把写入、失效和读路径放进更严格的事务和校验机制里。
Cache Aside 的核心是应用自己维护缓存:读时 cache miss 再查 DB 并回填;写时以 DB 为准,提交成功后删除缓存,避免缓存长期保留旧值。
删除缓存让下一次读基于数据库重建,逻辑简单,也避免复杂对象组装、部分字段更新和并发覆盖。直接更新缓存只有在数据构造简单且写路径可控时才更合适。
先删缓存再改 DB 的风险是:缓存刚被删除,并发读查到旧 DB 值并回填,随后写请求才提交新 DB 值,旧缓存会停留到过期。先改 DB 再删缓存通常把这个窗口缩小。
更新 DB 成功但删除缓存失败时,会出现旧缓存。常见兜底是较短 TTL、同步重试、异步重试队列、订阅 binlog 失效缓存,以及对失败任务的告警和人工修复。
商品详情、用户资料通常能接受短暂最终一致;余额、库存扣减、权限变更的错误成本更高,需要版本校验、事务边界、写串行化或直接读主库,不能只靠普通缓存策略。
一致性方案还要防止缓存失效后大量请求打到数据库。热点 key 可以用互斥重建、逻辑过期、随机 TTL、预热和限流,避免一致性处理引发可用性问题。
因为先删缓存会让并发读有机会把数据库旧值重新写回缓存。先更新数据库再删除缓存也不是完美,但旧值窗口通常更小,且更容易靠重试和 TTL 收敛。
它试图覆盖写请求期间并发读回填旧缓存的窗口:写前删一次,写后等待一小段时间再删一次。但延迟时间不可靠,仍要配合 TTL、重试和监控。
它把数据库变更作为事实来源,异步消费变更事件来删除或更新缓存。适合补偿应用删除失败,也能让多服务共享同一套缓存失效逻辑。
当错误成本高、不可短暂读旧值时,例如余额、权限、库存扣减。此时要提高读写一致性,使用事务、版本校验、串行化写入或直接绕开缓存。