第 12 章 · 缓存设计三大问题 + 一致性

四个交互演示:① 三大问题对比 · ② 布隆过滤器 · ③ 一致性顺序对比 · ④ 一致性方案对比

穿透 / 击穿 / 雪崩 三栏并排模拟

每栏分别模拟一种问题。蓝色请求从 Client 出发,到达 Cache 后判断 hit / miss。 红色请求 表示打到 DB 的请求(越多越危险)。 切换「无防护 / 有防护」对比效果。

① 穿透 Penetration

客户端不停查不存在的 ID(如 user:-1)
flow
📱 Client
Cache
(空)
🗄 DB
请求 0 打 DB 0 命中 0 拦截 0

② 击穿 Hotspot

热点 sku:1001 突然过期,并发查询同一 key
flow
📱 Client
Cache
(已过期)
🗄 DB
请求 0 打 DB 0 命中 0 等待 0

③ 雪崩 Avalanche

100 个 key 同一秒过期 / TTL 抖动
flow
📱 Client
Cache
(100 keys)
🗄 DB
请求 0 打 DB 0 命中 0 过期 0/100
观察建议:对比「无防护」与「有防护」的「打 DB」计数差距。生产中 DB 每秒可处理几千 QPS,超过这个值就开始报警,超过 1 万就可能宕机。

布隆过滤器交互:插入 / 查询 / 假阳性

位数组长度 m 和 hash 函数数 k 都可调。点「插入」会用 k 个 hash 函数把对应位置 1; 点「查询」高亮被检查的 k 个位,「全为 1」就判定「可能存在」。 多插入几个元素后再查从未插入过的字符串,会观察到「假阳性」现象。

m =128 k =3 已插入 n =0 置位率 =0% 理论误差率 =0%

操作日志

关键语义: 全部位为 1 = 「可能存在」(也可能是别的元素留下的痕迹 → 假阳性); 任一位为 0 = 「一定不存在」。 所以布隆过滤器**没有假阴性,只有假阳性**。

「先更新 DB 还是先删缓存」并发场景对比

同一组并发动作,按 4 种顺序排列,看哪种会出现「读到旧值并写入缓存」的不一致。 初始:DB = v_old,Cache 已失效。 写线程 W 想把 DB 更新成 v_new;同时读线程 R 触发缓存 miss。

结论:先更新 DB → 再删缓存」是 4 种顺序中最稳的。 它依然有极小窗口可能不一致(读 miss 比写更早开始,但写回比写完晚),生产中再叠加 延迟双删 进一步收敛。其他三种都有较大概率脏数据,不推荐。

四种缓存一致性方案对比

点击卡片查看每种方案的优缺点、适用场景。

点击上方任一卡片查看详情。

选型口诀:大多数场景用 Cache Aside(80%); 多系统共享缓存或拒绝改业务代码用 Binlog 订阅; 强一致需求用 Read/Write Through;高频小写入聚合用 Write Behind