2211 字
约 7 分钟
8
S25-J10 · Redis 与缓存题库:模式、三座大山与命中率数学

适用读者:准备架构师/资深后端面试,被问"你的缓存怎么设计"的人。
前置:S12(Redis/NoSQL)、S25-J09(MySQL)、J07(三层骨架)。
覆盖旧文:2699、2816/2830/2832/2834/2842、3466–3470、3572(Redis 面试题,9 域 50+ 问)→ 12 篇 / 约 12 万字符。
扩展点:E-S25-030(五种缓存模式与一致性真实表现)、E-S25-031(命中率与 RT 的非线性关系)、E-S25-032(过期删除 × 内存淘汰 × bigkey)、E-S25-033(Redis 快:单线程与多线程的分工)。
上一篇:S25-J09 · MySQL 题库讲评:索引、MVCC、锁与两阶段提交的机制链
下一篇:题库层第 5 篇 · 消息队列与 Kafka 题库(待写)。


1. 一句话结论

大多数候选人只记得两三种缓存模式,却不知道"很多缓存模式本身就是一致性问题的制造者"。 面试的标准路径是:先定模式 → 再定失效 → 最后定位(穿透/击穿/雪崩)。

出处:A-3469(缓存模式)、A-3466(一致性方案)、A-3467(淘汰与命中率)、A-3468(过期删除)、A-3572(题库)。


2. 五种缓存模式(E-S25-030)

模式 读写路径 一致性 典型问题
Cache Aside 读:缓存→miss 查库回填;写:先更库再删缓存 中 并发下"删缓存后旧值被回填"
Read Through 缓存层代理读库 中 依赖缓存组件能力
Write Through 写缓存同时写库(同步) 高 写延迟高
Write Behind 写缓存后异步批量刷库 低(有丢失窗口) 宕机丢数据
双删 / 延迟双删 更新前删 → 更库 → 延迟再删 中高 延迟时间难定;靠补偿

四种"一致性组合"的真实表现:

先删缓存、再更库   → 并发读会把旧值回填(脏数据长期存在)❌
先更库、再删缓存   → 极端并发下仍可能读到旧值(窗口极短)⚠️ 主流
只更库不删缓存     → 永久不一致 ❌
延迟双删 + 重试    → 把窗口压到毫秒级,仍需兜底 ✅ 工程常用

判据(必答):缓存一致性没有"绝对方案",只有"可接受的不一致窗口 + 兜底对账"。 答"用双删就一致了"会被追问到底。

兜底三件套:① binlog 订阅(Canal)异步删缓存;② 定时对账修复;③ 版本号/时间戳校验(写入带版本,读到旧版本触发回源)。

热点 key 的特殊处理:单 key 打满单分片 → 本地缓存(Caffeine)+ 多级缓存 + key 打散(key:{0..N})。


3. 命中率数学:为什么 10% 的跌幅要命(E-S25-031)

$$T = h \cdot T_{cache} + (1-h)\cdot T_{db}$$

取 $T_{cache}=5,\text{ms}$、$T_{db}=500,\text{ms}$:

命中率 h 平均 RT 相对 90%
95% 29.75 ms 0.55×
90% 54.5 ms 1.00×
80% 104 ms 1.91×
50% 252.5 ms 4.63×

复算:$h=0.9$ → $0.9\times5+0.1\times500=54.5$;$h=0.8$ → $4+100=104$。命中率跌 10 个点,RT 翻倍——这就是"命中率是缓存第一指标"的算术依据。

反向边界:并非所有系统都该追高命中率。GPU 生图(单次 10 s)里 10% 的重复率也值得缓存——收益看"省下的成本 × 命中率",不是命中率本身。


4. 三座大山:三种不同的失败

问题 触发条件 后果 解法
穿透 查不存在的数据(缓存永远 miss) 请求全打到 DB 空值缓存(短 TTL)、布隆过滤器、参数校验
击穿 单个热点 key 过期瞬间 瞬时并发全打到 DB 互斥重建(SETNX 锁)、逻辑过期(不真过期,后台续期)
雪崩 大量 key 同时过期 或 Redis 宕机 DB 被打挂 TTL 加随机抖动、多级缓存、限流降级、高可用集群

判据:穿透是"数据不存在",击穿是"单点失效",雪崩是"面失效"。 三者的解法不能互换。


5. 过期删除 × 内存淘汰(E-S25-032)

这是最容易被混淆的一对概念:

过期删除 内存淘汰
触发条件 key 到了 TTL 内存达 maxmemory
对象 单个过期 key 任意 key
策略 惰性删除(访问时查)+ 定期删除(每 100 ms 抽样) noeviction / allkeys-lru / volatile-lru / allkeys-lfu / volatile-ttl / random

定期删除如何控制开销:每轮从过期字典随机抽样(默认 20 个),若过期比例 > 25% 则重复本轮,并受时间上限(默认 25% CPU 时间)约束——用"抽样 + 时间片"把开销封顶,代价是"过期了但没被删"。

由此推出四个深水问题:

问题 原因 处理
内存没超但 RSS 高 惰性删除 + 未抽样到 主动 SCAN 清理、设 maxmemory 兜底
bigkey 过期卡顿 删除大 key 是同步释放,阻塞主线程 UNLINK(异步释放)、拆分 bigkey
主从过期不一致 从库不主动过期,等主库的 DEL 传播 读到逻辑过期 key 时从库返回空但保留(3.2+ 行为),业务侧容忍
持久化文件里的过期数据 RDB/AOF 保存了已过期 key 加载时过滤(主库);AOF 重写会清理

bigkey 判定:String > 10 KB、集合元素 > 5,000(经验值,非标准);redis-cli --bigkeys、MEMORY USAGE key。


6. Redis 为什么快(E-S25-033)

原因 说明
纯内存 无磁盘 IO(除持久化)
单线程命令执行 无锁、无上下文切换、无竞态
IO 多路复用 epoll 单线程管理万级连接
高效数据结构 SDS、跳表、listpack、渐进式 rehash
多线程分工 6.0+ 网络 IO 多线程(读写解析),命令执行仍单线程

高频追问:为什么命令执行不用多线程?——瓶颈不在 CPU 而在 IO/内存,多线程会引入锁与上下文切换;但删除大 key、AOF fsync 等耗时操作才用后台线程/子进程。


7. 集群与高可用:三句话答清

主从:全量(RDB)+ 增量(repl_backlog 偏移量);异步复制 → 可能丢数据
哨兵:监控 + 主观/客观下线 + Raft 式选主(quorum)
集群:16384 槽分片;客户端按 MOVED/ASK 重定向;槽迁移期间 ASK

一致性边界:Redis 主从/集群是"最终一致";WAIT 只能减少丢失窗口,不是强一致。分布式锁要用 SET key val NX PX + 唯一 value + Lua 释放,且注意锁续期(watchdog)与主从切换丢锁(Redlock 争议)。


8. 反例:四种常见错答

说法 问题
"先删缓存再更新数据库" 并发回填脏数据,最经典的错误方案
"用 SETNX 做分布式锁就安全了" 无唯一 value、无续期、主从切换丢锁
"Redis 6 多线程了,所以命令并发执行" 只有网络 IO 多线程,命令仍单线程
"过期 key 会被立刻删除" 惰性 + 定期抽样,存在"已过期未删除"窗口

9. 自测

  1. 五种缓存模式里,哪一种"本身就是一致性问题制造者"?为什么?
  2. 命中率从 90% 掉到 80%,RT 变化多少倍?公式是什么?
  3. 穿透 / 击穿 / 雪崩 各对应什么解法?为什么不能互换?
  4. bigkey 过期为什么会卡顿?怎么缓解?
  5. Redis 6 的多线程用在哪、不用在哪?
答案
  1. Cache Aside 的"先删缓存后更库"变体与 Write Behind(异步刷库);前者回填脏数据,后者有丢失窗口。
  2. 约 1.91 倍;$T=h\cdot T_c+(1-h)\cdot T_{db}$。
  3. 穿透=空值/布隆;击穿=互斥重建/逻辑过期;雪崩=TTL 抖动/多级/限流。
  4. 删除是同步释放内存,阻塞单线程;用 UNLINK 与拆分。
  5. 网络读写解析多线程;命令执行仍单线程。

10. 下一篇

题库层第 5 篇 · 消息队列与 Kafka 题库 —— 为什么用 MQ、顺序与幂等、Kafka 高性能的四个原因、延迟消息的三种实现(待写)。


本篇属于系列《S25 · 求职与面试》。旧文来源:A-2699、A-2816/2830/2832/2834/2842、A-3466…3470、A-3572;扩展点 E-S25-030…033。命中率数字为按公式复算,未经线上实测。

S25-J10 · Redis 与缓存题库:模式、三座大山与命中率数学
https://www.clxhxhhr.top/posts/5591/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。