面试知识库
极高 困难

缓存与数据库一致性#

一句话答案#

Cache Aside 模式:读时先查缓存未命中查 DB 写缓存,写时先更新 DB 再删除缓存,删而非更新避免并发覆盖。更可靠的方案用 Canal + MQ 异步删缓存。

核心要点

Cache Aside Pattern(旁路缓存)#

读流程: 先读缓存 → miss → 读 DB → 写缓存 写流程: 先更新 DB → 再删除缓存

为什么删而不更新缓存?

  1. 更新可能被并发覆盖(A 更新缓存后 B 立刻覆盖,但 B 的 DB 数据比 A 旧)
  2. 懒加载——不一定有人来读,白算了
  3. 缓存值可能是多表聚合结果,更新成本高

”先删缓存还是先更 DB”的争论#

方案不一致场景概率
先删缓存→更 DB线程 A 删缓存,线程 B 读 miss 回填旧值,A 再更 DB → 缓存是旧值(读远多于写)
先更 DB→再删缓存线程 A 读 DB 旧值(缓存刚好失效),线程 B 更 DB 并删缓存,A 再回填旧值 → 缓存是旧值极低(需要读比写慢)

结论: “先更 DB→再删缓存”更安全,不一致窗口更短。

延迟双删#

1. 删除缓存
2. 更新 DB
3. 延迟 N 毫秒后再次删除缓存(覆盖可能的旧值回填)
plaintext

延迟时间怎么定?

N = 主从同步延迟 + 一次读请求耗时 + 冗余(如 100ms)
通常 300ms ~ 1s
plaintext

问题:

  • 延迟时间难以精确计算
  • 第二次删除失败怎么办?→ 需要重试机制
  • 增加了系统复杂度

Canal + MQ 方案(推荐)#

应用更新 DB → MySQL binlog 变更

          Canal 监听 binlog

          发送删除消息到 MQ(RocketMQ / Kafka)

          消费者消费消息 → 删除 Redis 缓存

          删除失败 → MQ 重试(最多 N 次)

          重试耗尽 → 告警 + 人工处理
plaintext

为什么比延迟双删更可靠?

维度延迟双删Canal + MQ
可靠性依赖延迟时间估算基于 binlog,事件驱动
重试需自己实现MQ 原生支持重试 + 死信队列
侵入性业务代码耦合删除逻辑业务无感,独立组件监听
延迟固定延迟不精确binlog 近实时(毫秒级)
复杂度高(需部署 Canal + MQ)

Canal 挂了怎么办?

  • Canal 支持 HA(主备切换)
  • binlog 有位点记录,重启后从断点继续
  • 兜底:定时全量对账任务(每小时比对 DB 和缓存热点数据)

MQ 消费失败怎么办?

  • 重试 3-5 次(指数退避)
  • 重试耗尽进死信队列
  • 死信队列触发告警 → 人工处理
  • 缓存设 TTL 作为最终兜底(即使删除失败,TTL 到期后自动过期)

最终一致 vs 强一致的业务权衡#

场景一致性要求方案
商品详情页最终一致(秒级延迟可接受)Cache Aside + TTL
库存数量强一致不走缓存,直接查 DB / Redis 做主存储
用户信息最终一致Canal + MQ 异步删除
排行榜最终一致(分钟级可接受)定时刷新

核心原则: 大部分业务最终一致就够了,为强一致付出的成本(性能、复杂度)往往不值得。

面试回答(2分钟版)

缓存一致性我用 Cache Aside 模式,写时先更新 DB 再删除缓存——先更 DB 再删缓存比先删缓存更安全,因为”读 DB 比写 DB 慢”的概率极低。删除失败的问题,简单场景用延迟双删(延迟 = 主从同步时间 + 读耗时 + 冗余,通常几百毫秒),但时间难精确且需自己实现重试。更可靠的方案是 Canal + MQ:Canal 监听 MySQL binlog,变更事件发到 MQ,消费者负责删除缓存。失败了 MQ 自动重试,重试耗尽进死信队列告警。业务代码完全无感,缓存删除逻辑解耦。最终兜底是给缓存设 TTL——即使所有机制都失败了,TTL 到期后数据也会刷新。不过大部分业务最终一致就够了,库存这种需要强一致的就不走缓存。

追问与易错

追问方向:

  • “延迟双删的延迟时间怎么定?”→ 主从延迟 + 读耗时 + 冗余,通常 300ms-1s
  • “Canal 是什么?原理?”→ 伪装为 MySQL 从库,读取 binlog 解析出数据变更事件
  • “这套方案能保证强一致吗?”→ 不能,只保证最终一致。强一致需要分布式锁或不用缓存
  • “缓存和 DB 都更新失败怎么办?”→ DB 更新失败直接回滚不删缓存;缓存删除失败 MQ 重试

易错点:

  • ❌ “Cache Aside 保证强一致”——只保证最终一致
  • ❌ “延迟双删万能”——时间难定、删除可能失败、增加延迟
  • ❌ 不设 TTL 兜底——TTL 是最后一道防线