Redis 缓存深度话题
三大问题 · 5 大数据结构 · 缓存一致性 · 持久化 · 分布式锁
🎬 怎么引出
「Redis 三大缓存问题我能结合场景讲 — 穿透(查不存在的数据,绕过缓存打 DB),用布隆过滤器或缓存空值;击穿(热点 key 过期瞬间大量请求打 DB),用互斥锁或热点 key 永不过期;雪崩(大量 key 同时过期),过期时间加随机。缓存一致性我倾向延迟双删兜底。Redis 是后端面试高频,也是全栈必懂。」
1
三大缓存问题(必考)
| 问题 | 现象 | 解法 |
|---|---|---|
| 穿透 | 查不存在的数据(恶意攻击 id=-1),缓存和 DB 都没有,每次都查 DB | ① 缓存空值(短过期);② 布隆过滤器拦截不存在 |
| 击穿 | 热点 key 过期瞬间,大量并发请求同时打 DB | ① 互斥锁(只放一个查 DB);② 热点 key 永不过期(逻辑过期) |
| 雪崩 | 大量 key 同时过期,或 Redis 宕机,请求全打 DB | ① 过期时间加随机;② Redis 集群高可用;③ 限流降级 |
🧠 区分记忆
穿透 = 查的东西压根不存在(穿过去打 DB);击穿 = 一个点(热点 key)破了,集中冲击;
雪崩 = 一大片(很多 key)同时垮,整体崩塌。
名字就是画面。
⚠️ 布隆过滤器
判断元素可能在集合里或一定不在(不会漏报,可能误报)。原理:多个 hash 映射到位图。用于穿透防御:请求来先问布隆过滤器"这个 key 存在吗",不存在直接拒绝(不查 DB)。缺点: 有误报(可能放行不存在的)、不能删除(改进:布谷鸟过滤器)。
2
5 大数据结构 + 场景
| 类型 | 特点 | 典型场景 |
|---|---|---|
| String | 最基础,k-v | 缓存对象、计数器、分布式锁 |
| Hash | 字段-值映射 | 对象存储(user:name/user:age)、购物车 |
| List | 有序列表(双向链表) | 消息队列、最新动态、栈/队列 |
| Set | 无序集合(去重) | 标签、共同好友、去重、抽奖 |
| ZSet(有序集合) | 带 score 排序 | 排行榜、延时队列、Top N |
经典场景: ① 排行榜 = ZSet(ZADD 加分,ZRANGE 取前 N);② 点赞/收藏 = Set(SADD/SCARD);③ 购物车 = Hash(userid → {商品id: 数量});④ 分布式锁 = String SET NX;⑤ 限流 = List/Sorted Set 记录请求时间。
3
缓存一致性(双删/延迟双删)
⚠️ 经典问题
更新数据时,先更 DB 还是先删缓存?各有并发问题:
- 先删缓存再更 DB: 删了缓存,另一请求查 → 查 DB(旧值)→ 写回缓存(旧值)→ 更新 DB。缓存还是旧值。
- 先更 DB 再删缓存: 更新 DB,删缓存前另一请求查 → 命中旧缓存 → 返回旧值。短暂不一致。
主流方案:Cache Aside(旁路缓存) — 读先查缓存没有查 DB 写回;写先更 DB 再删缓存。延迟双删兜底:删缓存 → 更 DB → sleep 一会 → 再删一次缓存。
关键认知: 强一致性用不上缓存。缓存只能保证最终一致。要强一致(如金融)走 DB 或加锁,别用缓存。面试答这个体现你懂权衡。
4
持久化:RDB vs AOF
| 维度 | RDB | AOF |
|---|---|---|
| 方式 | 定期快照(全量) | 追加命令日志(增量) |
| 恢复速度 | 快(直接加载) | 慢(重放命令) |
| 数据安全 | 可能丢(两次快照间) | 更全(每次写都记) |
| 文件大小 | 小(压缩) | 大(需 rewrite 瘦身) |
| 推荐 | 备份/容灾 | 不丢数据 |
生产实践: 通常 RDB + AOF 都开。AOF 用 everysec(每秒刷盘,平衡性能和安全,最多丢 1 秒)。Redis 4.0+ 支持混合持久化(RDB 全量 + AOF 增量),恢复快且数据全。
5
分布式锁
🔑 基础:SET NX + 过期
# 原子操作:不存在才设置 + 过期
SET lock_key unique_value NX PX 30000
# 释放锁:必须用 Lua 保证"判断值+删除"原子
if redis.get("lock_key") == unique_value then
return redis.del("lock_key")
else
return 0
end
⚠️ 三个坑
① 必须设过期时间,否则持锁进程崩了,锁永远不释放(死锁);② 释放锁要校验 value(unique_value),否则可能删别人的锁;③ 业务执行超过过期时间,锁被自动释放,其他进程拿到锁,原进程回来删了新锁 → 用看门狗续期(Redisson 实现)。
生产用 Redisson:封装了可重入、看门狗续期、Lua 原子操作。Redlock(多节点)用于对可靠性要求极高的场景,但有争议(Martin Kleppmann 批评过)。
7
高频追问
1缓存穿透/击穿/雪崩区别和解法? 必问
穿透: 查不存在的数据,缓存空值或布隆过滤器。击穿: 热点 key 过期,互斥锁或永不过期。雪崩: 大量 key 同时过期或 Redis 挂,过期时间加随机 + 集群高可用 + 限流降级。
💬 名字即画面:穿透=穿过去、击穿=一点突破、雪崩=大面积垮。
2缓存和 DB 一致性怎么保证? 高频
主流 Cache Aside:先更 DB 再删缓存,允许短暂不一致。兜底延迟双删(删缓存→更 DB→sleep→再删)。关键认知:缓存只能最终一致,要强一致别用缓存。还有订阅 binlog(Canal)异步刷缓存,但复杂度高。
3Redis 为什么快? 高频
① 纯内存(比磁盘快几个数量级);② 单线程避免锁竞争和上下文切换(6.0 后网络 IO 多线程,命令执行仍单线程);③ IO 多路复用(epoll);④ 高效数据结构(SDS、跳表、压缩列表)。单机 10 万+ QPS。
4RDB 和 AOF 区别?怎么选? 中等
RDB 快照(全量,恢复快可能丢),AOF 日志(增量,恢复慢数据全)。生产两个都开,AOF 用 everysec(最多丢 1 秒),4.0+ 混合持久化(RDB 全量+AOF 增量)。备份用 RDB,不丢数据用 AOF。
5分布式锁怎么实现?有什么坑? 高频
SET key value NX PX 过期原子加锁。三个坑:① 必须过期时间(防死锁);② 释放要校验 value 防删别人的(Lua 原子);③ 业务超时锁被释放,用看门狗续期。生产用 Redisson(封装可重入/续期/Lua)。Redlock 多节点高可靠但有争议。6ZSet 为什么用跳表不用红黑树? 深问
跳表实现简单(链表+多层索引),范围查询友好(ZRANGE 顺着链表走),内存可控。红黑树范围查询要中序遍历复杂。作者 Antirez 解释:跳表更简单、范围操作更高效、改并发更容易。这是 Redis 设计哲学。
8
30 秒陈述
🎤 30 秒版
「Redis 三大问题我能结合场景讲:穿透(查不存在的)用布隆过滤器,击穿(热点 key 过期)用互斥锁,雪崩(大量 key 同时过期)过期时间加随机。一致性用 Cache Aside 先更 DB 再删缓存,延迟双删兜底,但强一致别用缓存只能最终一致。分布式锁 SET NX + 过期 + Lua 释放,生产用 Redisson 带看门狗续期。持久化 RDB+AOF 都开。Redis 快是因为纯内存 + 单线程无锁 + IO 多路复用。」