常见的 Redis 面试题

本次导航

  • 为什么快,6.0 之后还算不算单线程
  • 过期 Key 什么时候删,内存满了谁被踢
  • RDB、AOF 怎么选
  • 锁、槽、事务这几问怎么答到点子上
  • 穿透、击穿、大 Key,面试里通常听到哪一层

发车前提醒:下面每题先给能说出口的答法,再补一点别被追问时露怯的细节。

一、Redis 为什么快?现在还是单线程吗?

可以按三层说,别只甩一句"单线程没有锁"。

  1. 数据在内存里,读写不走磁盘随机 I/O。
  2. 命令执行是单线程 ,没有多线程改同一份数据的锁竞争,INCR 这类操作天然不会加到一半。
  3. 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 回去。

面试说到"先库后删缓存 + 可接受的短暂不一致"就够,强一致要上别的方案,那是系统设计问题。

主从延迟也会读到旧缓存或旧库,读要走主、或者接受短暂落后,别假装复制是同步的。

九、问自己几个问题

  1. 用自己的话讲清:命令执行单线程,I/O 可以多线程。
  2. SET lock:x a NX EX 5,另开窗口再抢一次;再用 Lua 按值删除,换一个值删,应该是 0。
  3. CONFIG GET maxmemory-policy,说出当前策略满了会怎样。
  4. 写一个带 {user1001} 的两个 Key,解释它们为什么能在 Cluster 里一起 MGET。

下期预告:Redis 完结篇,把线上真正扎手的那些问题放到项目路径里:热点商品、大 Hash、缓存和 DB 打架、瞬间打满连接。


本次导航结束,欢迎 关注、点赞、转发。

相关推荐
鱼宵3 小时前
LangChain4j 生产化改造:记忆落 Redis、向量落 ES,重启再也不丢
数据库·redis·elasticsearch·langchain4j
码匠许师傅5 小时前
【C++三方组件】redis-plus-plus:现代 Redis C++ 客户端
java·c++·redis
豆角焖肉6 小时前
SpringBoot + Redis 缓存实战
java·spring boot·redis·后端·缓存·菜单缓存
来福猿6 小时前
分布式锁 Redis 实现源码分析:SETNX、过期时间与可重入设计
数据库·redis·分布式
行百里er20 小时前
Redis 模块——JSON、向量搜索与时序
redis·后端
蓝胖的四次元口袋20 小时前
Redis知识梳理(1)
数据库·redis·缓存
wefg121 小时前
【Redis】C++ 自定义客户端
数据库·redis·缓存
一个有温度的技术博主1 天前
两级缓存一致性实战:Redis + 本地缓存,如何不让数据“分裂”?
redis·spring·缓存
码途行者1 天前
缓存一致性难题:Redis 和数据库如何做到数据一致?
数据库·redis