Redis 面试连环追问(口述链)
一条真实面试里很常见的追问路径。目标不是再学一遍知识点,而是:从上到下能连续讲 8~15 分钟,每一环都能接住下一问。
详细专题仍看分区文章;本页是「串起来怎么说」。
练习方式:先看稿 → 只看小标题 → 脱稿。卡在哪一环,回对应专题补。
整条链(先看地图)
Redis
↓ 为什么快
↓ 单线程
↓ Redis 6
↓ 数据结构
↓ ZSet 排行榜
↓ 缓存
↓ 缓存穿透
↓ 缓存击穿
↓ 分布式锁
↓ SET NX
↓ 为什么要 EX / PX
↓ 为什么要唯一 value
↓ Lua
↓ Redisson
↓ 看门狗
↓ 缓存一致性
↓ MySQL
↓ MQ1. Redis 是什么?(开场)
Redis 是基于内存的高性能键值库。项目里我主要用它做缓存、分布式锁、计数、限流和临时状态,不是当业务主库替代 MySQL。
展开一句即可,别一上来背全部命令。
2. 为什么快?
口述四条就够:
- 内存读写,比磁盘快几个数量级
- 高效数据结构(哈希、跳表等)
- I/O 多路复用,单线程也能扛很多连接
- 命令简单,多数复杂度低
加一句边界:
快不等于不会慢。大 Key、热 Key、
KEYS *、慢命令一样能把实例拖垮。
3. 单线程?那不会成为瓶颈吗?
说的「单线程」主要指命令执行在一个主线程里串行跑,所以单命令原子、不用复杂锁。
但进程里不只有这一个线程;而且一个慢命令会堵住后面所有请求。
生产禁忌:KEYS *、胡乱 HGETALL 超大 Hash、同步 DEL 超大 Key(改用 SCAN / UNLINK)。
4. Redis 6 呢?不是多线程了吗?
Redis 6 可以把部分 网络读写 交给 I/O 线程并行,减轻读写 socket 的压力。
命令执行仍然主要在主线程,业务上还是按「命令串行」来设计,不能假设多个写逻辑在服务端并行交错执行同一把锁。
5. 常用数据结构?举个例子
快速带过五件套 + 特殊类型:
| 类型 | 我怎么用 |
|---|---|
| String | 缓存 JSON、计数、SET NX 锁 |
| Hash | 对象字段、可部分更新 |
| List | 简单队列、最新列表 |
| Set | 去重、共同好友 |
| ZSet | 排行榜、延迟任务 |
| Bitmap / HLL | 签到、近似 UV |
| Stream | 轻量消息流 |
6. ZSet 怎么做排行榜?
Key 是榜单,member 是用户/内容,score 是热度或分数。
ZINCRBY更新分数,ZREVRANGE取 TopN。
可追问时补:
- 日榜/周榜用不同 Key
- 榜太大要裁剪
- 并列分数靠 member 或次级排序策略
- 底层小数据压缩结构,大数据 跳表 + 字典(范围查询友好)
7. 缓存一般怎么用?
读多写少的热点数据放 Redis。典型路径:先查缓存,未命中查 MySQL,回写缓存并设过期(最好加随机,防雪崩)。
架构演进可一句带过:单 Redis → 热点加本地缓存(Caffeine)→ 保护 DB。
8. 缓存穿透是什么?怎么防?
查根本不存在的数据:缓存没有,DB 也没有,每次都打穿到 DB(常被恶意扫库)。
防:
- 缓存空值(短 TTL)
- 布隆过滤器(说不存在就一定不存在;说存在可能误判)
9. 缓存击穿呢?和穿透有什么区别?
热点 Key 刚好过期,瞬时大量请求打到 DB。
穿透是「数据本来就不存在」;击穿是「热点存在但缓存空窗」。
防:
- 互斥锁:只有一个线程回源,其它等待重试
- 逻辑过期:先返回旧值,后台异步刷新
(若再问雪崩:大量 Key 同时过期或 Redis 挂了 → 过期随机 + 限流降级 + 高可用。)
10. 分布式锁为什么用 Redis?
多实例下 JVM 锁管不住。Redis 单线程执行命令 +
SET key value NX PX,适合做跨进程互斥(库存预扣、防重复提交、定时任务抢主等)。
边界:金融级强一致可考虑 ZK;多数业务 Redisson + DB 乐观锁兜底即可。
专题:分布式锁
11. SET NX 是什么意思?
NX= Not eXists:Key 不存在才设置成功 → 抢到锁;已存在则失败 → 没抢到。
完整推荐写法:
SET lock_key unique_value NX PX 30000不要拆成先 SETNX 再 EXPIRE。
12. 为什么一定要 EX / PX(过期)?
防止持锁进程宕机后死锁。
若SETNX成功后、还没EXPIRE就挂了,锁永远不释放。所以 NX 和 PX 必须同一条命令原子完成。
13. 为什么 value 必须唯一?
防止误删别人的锁。
场景:A 超时锁过期 → B 拿到锁 → A 业务结束执行DEL,会把 B 的锁删掉。
做法:value = UUID(+ 线程标识);释放前校验「是不是我的」,是才删。
14. 那为什么还要 Lua?
「先 GET 判断再 DEL」在客户端是两步,中间仍可能被打断,极端下仍会误删。
用 Lua 把校验 + 删除做成一次原子脚本 交给 Redis 执行。Redisson 的unlock就是这么干的。
Lua 也常用于:库存判断+扣减、限流窗口计数。专题:并发
15. 为什么还要 Redisson?自己 SET 不够吗?
手写锁能讲清原理,生产更稳的是 Redisson:可重入、解锁封装、看门狗续期、少踩续期和误删的坑。
16. 看门狗是什么?
业务可能超过初始过期时间。看门狗在持锁期间周期性续期,避免锁提前释放导致并发不安全。
进程挂了,看门狗也停,锁最终过期,避免永久死锁。unlock放finally;正常释放后不再续期。
17. 缓存和 MySQL 怎么保证一致?
常用 Cache Aside:先更新 MySQL,再删除缓存(让下次读取回源)。
一般不采用「先更新缓存再写库」或「写库后更新缓存」当默认方案——并发下更容易脏数据,且写多读少时浪费。
并发下仍可能有极短不一致;普通业务可接受。要求更高:
- 延迟双删(慎用,要懂窗口)
- Canal 听 MySQL Binlog → 发 MQ → 消费者删缓存,靠重试做最终一致
专题:缓存架构实战
18. 这里 MQ 扮演什么角色?(链的收口)
分清两件事,面试官常混着问:
A. 用 Redis 当 MQ 行不行?
- Pub/Sub:不能当可靠队列(不在线即丢、不持久)。
- Stream:轻量可以,消费组/ACK 有了;订单等核心链路仍优先专业 MQ。
B. 缓存一致性链路里的 MQ:
- 不是用 Redis 替代 MQ,而是 Binlog → MQ → 删 Redis,保证删缓存可重试、最终一致。
专题:消息
一口气口述模板(约 2~3 分钟压缩版)
Redis 是内存键值库,我们主要做缓存和分布式锁。快是因为内存、高效结构和 I/O 多路复用;命令在主线程串行,所以单命令原子,但慢命令会堵全库。Redis 6 的多线程主要帮网络读写,命令执行仍偏单线程。
类型里排行榜用 ZSet。缓存先 Redis 后 MySQL,防穿透用空值和布隆,防击穿用互斥或逻辑过期,过期加随机防雪崩。
分布式锁用SET key uuid NX PX:NX 互斥,PX 防死锁,uuid 防误删,释放用 Lua 原子校验删除。生产用 Redisson,看门狗负责续期。
和 MySQL 一致一般是先更新库再删缓存;要更稳就 Binlog + MQ 异步删缓存。Redis Pub/Sub 不当可靠 MQ,核心异步还是专业消息中间件,Stream 只适合轻量场景。
自测清单
按链脱稿时,每一环能否用 一两句话 答完,并主动引出下一环:
- [ ] 为什么快 → 自然说到单线程
- [ ] 单线程 → 自然说到 Redis 6 边界
- [ ] 数据结构 → 主动举 ZSet 排行榜
- [ ] 缓存 → 自己区分穿透 / 击穿
- [ ] 锁 → NX、PX、唯一 value、Lua、Redisson、看门狗一条线
- [ ] 一致性 → MySQL 怎么写、MQ 在一致性里干什么、和「Redis 当 MQ」有何不同
卡哪一环,回上面链接的专题页补,再回到本链重练。
返回:Redis 总览