本次导航
- 为什么快,6.0 之后还算不算单线程
- 过期 Key 什么时候删,内存满了谁被踢
- RDB、AOF 怎么选
- 锁、槽、事务这几问怎么答到点子上
- 穿透、击穿、大 Key,面试里通常听到哪一层
发车前提醒:下面每题先给能说出口的答法,再补一点别被追问时露怯的细节。
一、Redis 为什么快?现在还是单线程吗?
可以按三层说,别只甩一句"单线程没有锁"。
- 数据在内存里,读写不走磁盘随机 I/O。
- 命令执行是单线程 ,没有多线程改同一份数据的锁竞争,
INCR这类操作天然不会加到一半。 - I/O 模型合适:epoll 多路复用,一个线程盯着很多连接。协议也简单,解析开销小。
6.0 起可以开 I/O 线程 做网络读写,命令执行仍在主线程。
所以更准确的说法是:执行模型还是单线程,网络可以并行。
面试官说"Redis 是单线程的",你补一句 I/O 线程,面试分+1。

追问:单线程为什么不会成为瓶颈?内存操作通常是微秒级,瓶颈多在网络和慢命令(KEYS *、超大 HGETALL),不在"一个核算不过来"。
真把 CPU 打满,先查大 Key、热 Key、Lua 死循环,而不是先上多线程 Redis。
二、Key 过期了,是马上删掉的吗?
不是。两套机制一起干活。
惰性删除:有人访问这个 Key,发现 TTL 过了,当场删,返回空。没人碰的 Key,可以在内存里继续躺着。
定期删除:后台按频率抽一批带过期时间的 Key,到期的清掉。抽不中的,继续等下一次,或等被访问时惰性删。

所以会出现:TTL 看着过了,INFO memory 一时半会儿没掉下去。不是 bug。
和过期容易混的是 淘汰 :maxmemory 到了,按策略踢 Key,跟 TTL 没有必然关系。
缓存场景常见:
| 策略 | 什么时候用 |
|---|---|
noeviction |
默认。写满直接报错,当数据库用时可以 |
allkeys-lru |
普通缓存,踢最久没人碰的 |
allkeys-lfu |
你明确知道有长期热点 |
volatile-lru |
只有设了过期的才允许踢,没 TTL 的当重要数据留着 |
CONFIG GET maxmemory-policy 能看到当前值。
只背 LRU 不够,要能说出 volatile 和 allkeys 的差别。
三、RDB 和 AOF 怎么选?
一句话:RDB 是拍照,AOF 是记账。
- RDB:某一时刻的内存快照,文件小,恢复快。两次快照之间的写入,崩溃就没了。
- AOF :把写命令追加到日志。
appendfsync everysec最常见,大概最多丢一秒。文件更大,恢复要重放。 - 混合:AOF 文件前面是 RDB 形态的底,后面跟增量命令。重启时先加载底再重放,兼顾速度和丢失窗口。
面试里别说"生产必须双开",缓存丢了能回源 DB,可以只开 RDB,甚至关持久化换性能。
订单、库存、Stream 这种丢了会心疼的,AOF everysec 更常见。
SAVE 会卡住主线程,线上用 BGSAVE。fork 出子进程那一下,内存大时可能抖一下,这是追问点。
四、分布式锁怎么写才不算裸奔?
最低配:
bash
SET lock:order_pay node-a NX EX 30
NX 保证只有一个人抢到,EX 防止进程死了锁永远不放。到这里只能算入门。
面试官接着会问:业务跑超时了,锁过期,B 抢到了,A 回来 DEL,会不会把 B 的锁删掉?会。所以解锁必须带身份:
lua
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
else
return 0
end
再往下:时钟、续期、Redlock。单实例 Redis 做缓存互斥、下单防重,SET NX EX + 这段 Lua 就够讲。
Redlock 要多活独立节点,争议也大,没踩过别主动展开。
锁不是银弹,扣库存这种 check-then-act,Lua 里读完再写,比锁更干净。
五、Cluster 的 16384 个槽是什么?
Cluster 把整个 keyspace 切成 16384 个槽,每个主节点负责一段。Key 落在哪个槽:
scss
CRC16(key) mod 16384
客户端算完槽,把命令发到对应节点。
加节点、迁数据,搬的是槽,不是"把某个前缀的 Key 手动拷过去"。
多 Key 命令(MGET、SUNION)要求这些 Key 在 同一个槽。
用花括号包住哈希标签:order:{1001}:pay 和 order:{1001}:items 都按 1001 算槽,才能一起操作。

从库、Sentinel 是另一套题:主从复制解决读扩展和备份,Sentinel 负责主挂了选新主。
Cluster 自己带故障转移和分片,别把三个词搅成一个"高可用"。
六、事务能回滚吗?Pipeline 算事务吗?
不能回滚,Pipeline 也不是事务。
- Pipeline:客户端把一串命令打包发出去,少几次网络往返。执行时别人可以插进来。
- MULTI / EXEC :
EXEC的时候这串命令连续跑完,中间不插别的客户端。某条因为类型错失败了,旁边那条照样提交。 - Lua:能在服务端写 if/else,执行期间同样不插队。失败也不会把已经写成功的命令撤掉。
入队时参数个数都写错,EXEC 会整单放弃(EXECABORT)。
执行期对字符串做 INCR,只炸那一条。
面试里把这两种失败分开,比背"Redis 没有事务"有用。
需要"读到库存再决定扣不扣",用 Lua,别指望 MULTI 里下一条能看到上一条的返回值------返回值要等 EXEC 才一次性给你。
七、穿透、击穿、雪崩,大 Key、热 Key
这组题喜欢混着问。先把对象说清:
| 发生了什么 | |
|---|---|
| 穿透 | 查一个库里从来没有的 ID,缓存没有,DB 也没有,每次空跑 |
| 击穿 | 一个热点 Key 刚好过期,所有人打到 DB |
| 雪崩 | 大批 Key 同一时刻过期,或 Redis 挂了 |
| 大 Key | 单个 Key 体积或元素特别多,删、迁、HGETALL 都会卡住别人 |
| 热 Key | 单个 Key 被打得特别凶,一个核、一块网卡先着 |
应对在面试里说到能落地的那一层就行:
- 穿透:空值短 TTL;流量脏就上布隆(
BF.EXISTS),过滤器说没有就别查库 - 击穿:
SET NX EX只放一个人回源;热点逻辑过期,先把旧值吐出去再刷新 - 雪崩:TTL 加随机数;Redis 挂了靠本地缓存和接口降级,不是再调一个过期时间
- 大 Key:拆;删除用
UNLINK别DEL - 热 Key:本地缓存、拆成多个副本随机读。Redis 8.6 可用
HOTKEYS START/GET找谁最热
项目里这些怎么排障、怎么改代码,这里先挖个坑,我们下期再说。
八、缓存和数据库哪个先写?
面试常考 Cache-Aside:
读:先 Redis,没有再 DB,再回填 Redis。
写:先更新 DB,再删 Redis 的 Key(让下次读回填),不要先改缓存再改库------库失败了缓存就是脏的。

删缓存也会有并发窗口:A 删完还没写完,B 读到旧值又 SET 回去。
面试说到"先库后删缓存 + 可接受的短暂不一致"就够,强一致要上别的方案,那是系统设计问题。
主从延迟也会读到旧缓存或旧库,读要走主、或者接受短暂落后,别假装复制是同步的。
九、问自己几个问题
- 用自己的话讲清:命令执行单线程,I/O 可以多线程。
SET lock:x a NX EX 5,另开窗口再抢一次;再用 Lua 按值删除,换一个值删,应该是 0。CONFIG GET maxmemory-policy,说出当前策略满了会怎样。- 写一个带
{user1001}的两个 Key,解释它们为什么能在 Cluster 里一起MGET。
下期预告:Redis 完结篇,把线上真正扎手的那些问题放到项目路径里:热点商品、大 Hash、缓存和 DB 打架、瞬间打满连接。
本次导航结束,欢迎 关注、点赞、转发。