IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
准备 Redis 面试,最怕碎片化背诵。我为你整理了50 道大厂高频 Redis 面试题,覆盖数据结构、缓存、持久化、高可用、分布式锁和性能调优六大模块,每题都附带答案要点,部分直接给出现场可用的 Python 代码示例。读完这 50 问,Redis 面试再无死角。
一、基础概念与数据结构(10 问)
1. Redis 为什么这么快?
纯内存操作:数据存在内存中,读写微秒级。
单线程模型:避免上下文切换和锁竞争,命令执行天然原子。
I/O 多路复用:基于 epoll/kqueue 模型,高效处理大量并发连接。
高效数据结构:String、Hash 等底层实现精巧(如 ZipList、跳表),数据紧凑。
2. Redis 的五种基本数据类型是什么?各自应用场景?
String:缓存对象、计数器、分布式锁、Session 共享。
Hash:存储对象(用户信息、商品详情),方便部分字段更新。
List:消息队列(
LPUSH+BRPOP)、最新列表、栈/队列。Set:标签系统、共同好友(交集)、抽奖去重。
Sorted Set:排行榜、带权重的消息队列、延时队列。
3. Redis 底层数据结构如何优化内存?(内部编码)
String:int(整型直接存)、embstr(短字符串连续分配)、raw(长字符串)。
Hash:字段少且值短用
ziplist(压缩列表),超出阈值转hashtable。List:3.2 后统一用
quicklist(ziplist节点组成的双向链表)。Set:全为整数时用
intset,否则hashtable。ZSet:元素少且值短用
ziplist,否则skiplist+dict。
4. 键的过期策略是怎样的?
惰性删除:访问 key 时检查是否过期,过期则删除。
定期删除:每隔 100ms 随机抽一批设了 TTL 的 key 检查并删除过期 key。
两者结合,兼顾 CPU 和内存。
5. 淘汰策略有哪些?LFU 和 LRU 的区别?
8 种淘汰策略:
noeviction(默认)、volatile-lru、allkeys-lru、volatile-random、allkeys-random、volatile-ttl,以及 Redis 4.0+ 的volatile-lfu和allkeys-lfu。LRU:最近最少使用,看“多久没被访问”。
LFU:最不经常使用,看“被访问次数多少”。LFU 更适合保留热数据。
6.DEL和UNLINK有什么不同?
DEL:同步阻塞删除,删除大 key 时可能阻塞 Redis。UNLINK:异步删除,将 key 从键空间移除,真正的内存回收由后台线程做,不阻塞主线程。Redis 4.0+ 支持。
7. 什么是 Redis 的 Pipeline?为什么能提升性能?
将多个命令打包成一次网络请求发送,服务端批量处理后一次性返回结果,大幅减少 RTT(网络往返时间)。
注意:Pipeline 不是原子操作,中间可能插入其他客户端的命令。
Python 示例:
pipe=r.pipeline()pipe.set('k1','v1')pipe.incr('counter')pipe.get('k1')results=pipe.execute()# [True, 1, 'v1']8.KEYS命令为什么禁用?该用什么代替?
KEYS *会遍历所有键,导致 Redis 长时间阻塞,生产禁用。替代:
SCAN游标迭代,每次返回少量 key,不会阻塞。SCAN 0 MATCH user:* COUNT 100。
9. Redis 是单线程的,为什么还能处理高并发?
Redis 6.0 之前:纯单线程模型执行命令,但 I/O 读写使用了多路复用(epoll),使得一个线程可以同时处理成千上万个客户端连接。
Redis 6.0 后引入多线程 I/O:网络数据的读写由多个 I/O 线程并行处理,命令执行仍是单线程,兼顾安全与性能。
10. Redis 事务的 ACID 特性如何?
原子性:事务中的命令要么都执行,要么(编译时错误)都不执行。但不支持回滚,运行时错误不会撤销已执行的命令。
没有隔离性:单线程串行执行,天然无脏读、不可重复读。
持久性:取决于持久化配置,默认不保证。
二、缓存设计难点(10 问)
11. 缓存穿透、缓存击穿、缓存雪崩的区别及解决方案?
穿透:查不存在的数据,请求直达 DB。
- 解决:缓存空对象(短 TTL)、布隆过滤器。
击穿:热点 key 过期,大量请求打到 DB。
- 解决:互斥锁(
SETNX查库重建)、逻辑过期异步刷新。
- 解决:互斥锁(
雪崩:大量 key 同时过期或 Redis 宕机。
- 解决:随机 TTL、多级缓存、限流降级、Redis 高可用。
12. 如何用布隆过滤器防止缓存穿透?Python 怎么实现?
布隆过滤器预先存所有合法 key,请求先查布隆过滤器,不在则直接拒绝,避免查库。
Python 示例(基于 Redis 位图):
class BloomFilter: def __init__(self, redis_client, key, expected_items, false_positive_rate):# 计算 bit 数组大小和哈希函数个数... def add(self, item):foroffsetinself._hash_offsets(item): self.redis.setbit(self.key, offset,1)def exists(self, item):returnall(self.redis.getbit(self.key, offset)foroffsetinself._hash_offsets(item))13. Cache Aside 模式是什么?为什么是“删除缓存”而非“更新缓存”?
读:先读缓存,miss 则读 DB 并写缓存。
写:先写 DB,再删缓存。
删缓存的原因:更新缓存可能产生并发写脏数据;删除后下次读会从 DB 加载最新数据,简单可靠。
14. 什么是延迟双删?解决了什么问题?
在写 DB 后先删一次缓存,等 DB 主从同步完成后(如 500ms)再删一次缓存,防止因主从延迟导致读请求把旧数据加载到缓存。
实现:
delete cache -> update DB -> sleep(延迟) -> delete cache again。
15. 如何保证缓存与数据库的最终一致性?
采用
Cache Aside+ 过期时间兜底。结合 MQ 或 binlog 异步通知刷新缓存(Canal + MQ)。
对于强一致性场景,直接读主库并设置较短过期。
16. 热点 key 怎么发现和处理?
发现:客户端统计、
redis-cli --hotkeys、MONITOR(临时)。解决:本地缓存(
cachetools)、读写分离、key 拆分(hotkey:1:0,hotkey:1:1随机分布)。
17. Redis 缓存与本地缓存的区别?各自的适用场景?
Redis 缓存:全局共享,独立于应用,适合分布式系统,更新后所有节点生效。
本地缓存(如 Python
cachetools):进程内,速度快(无网络开销),但容量小,各节点独立,适合极热数据或对一致性要求低的配置。
18. 如何设计一个缓存更新的定时任务?
扫描 DB 中变更的数据,更新或删除对应 Redis 键。
可以利用 Redis 的
SET带EX过期,让缓存自动失效;或通过PUB/SUB通知订阅者刷新。
19. 缓存为什么会出现竞态条件?怎么解决?
多个请求同时发现缓存失效,同时去查 DB 并写缓存,造成 DB 压力。
解决:加互斥锁(
SETNX),保证只有一个请求去查 DB 并重建缓存。
20.SETNX实现分布式锁有什么问题?
死锁风险(未设过期时间)。
锁过期后业务还在执行,导致其他客户端获取锁。
无法保证锁在集群环境下的强一致性。
三、持久化机制(5 问)
21. RDB 和 AOF 的原理与区别?
RDB:指定时间间隔生成内存快照(
BGSAVE),二进制压缩,恢复快,可能丢一段时间数据。AOF:记录每条写命令,实时性高(可配置
everysec),文件可读,但文件大、恢复慢。
22. 混合持久化是什么?有什么优点?
Redis 4.0+ 支持
aof-use-rdb-preamble yes,AOF 重写时将当前数据生成 RDB 格式写入 AOF 文件开头,后半部分为 AOF 增量命令。优点:恢复速度快(RDB 部分),数据完整(AOF 部分)。生产首选。
23. AOF 重写机制如何工作?
后台子进程
BGREWRITEAOF遍历当前数据,生成最小命令集写入新 AOF 文件。重写期间的新写入命令同时追加到旧 AOF 缓冲区和重写缓冲区。
子进程完成后,将重写缓冲区的增量追加到新文件,然后原子替换旧 AOF。
24. 如何选择持久化策略?
纯缓存(数据可重建)→ 关闭持久化。
一般业务(允许丢几分钟数据)→ 只开 RDB。
重要业务(最多丢 1 秒)→ 开 AOF
everysec。核心业务 → RDB + AOF + 混合持久化 + 异地备份。
25. AOF 文件损坏了怎么办?
Redis 自带
redis-check-aof --fix appendonly.aof,截断到最后一个有效命令。然后重启 Redis。
四、主从复制与高可用(8 问)
26. 主从复制全量同步和部分同步的流程?
全量同步:从节点发送
PSYNC ? -1,主节点BGSAVE生成 RDB 发给从节点,同时记录写命令到复制缓冲区,从节点加载 RDB 后执行缓冲命令。部分同步:从节点重连时带上
runid和offset,如果offset在主节点复制积压缓冲区范围内,就只传输缺失的命令。
27. 复制积压缓冲区是什么?设置多大合适?
一个固定大小的 FIFO 队列,记录最近写命令,用于部分重同步。
默认 1MB,可根据写入量和网络稳定性调大(如
repl-backlog-size 64mb)。
28. Sentinel 哨兵是如何工作的?
哨兵集群监控主从节点健康状态。
主观下线(SDOWN):单哨兵认为节点失联。
客观下线(ODOWN):多数哨兵认为主节点下线,选举 leader 执行故障转移。
故障转移:选出最优从节点提升为主,让其他从节点同步新主,通知客户端新主地址。
29. Sentinel 和 Cluster 的区别?
Sentinel:解决高可用,监控主节点故障自动转移,但数据全量存储,写入单点。
Cluster:解决数据分片和写入扩展,通过哈希槽将数据分散在多个主节点,每个主节点可配从节点提供故障转移。
30. Redis Cluster 的哈希槽是如何工作的?
共 16384 个哈希槽,
CRC16(key) % 16384决定键落在哪个槽。每个主节点管理一部分槽,客户端根据槽映射将请求发到对应节点。
Hash Tag
{...}可让多个 key 落到同一槽,以支持事务和批量操作。
31. MOVED 和 ASK 重定向的区别?
MOVED:键所属槽已永久迁移到另一节点,客户端应更新本地槽表后重试。ASK:槽正在迁移中,本次临时重定向到指定节点,客户端不更新槽表,需先发送ASKING再执行命令。
32. Python 如何连接 Redis Cluster 并实现高可用?
from redis.clusterimportRedisCluster rc=RedisCluster(host='localhost',port=6371,decode_responses=True)rc.set('key','value')Smart Client 自动处理 MOVED/ASK 重定向和故障转移。
33. 集群模式下 Lua 脚本有限制吗?
脚本中访问的所有 key 必须在同一个哈希槽,否则报错
CROSSSLOT。使用 Hash Tag 将相关 key 映射到同一槽可解决。
五、分布式锁(7 问)
34. 用 Redis 实现分布式锁的演进过程?
SETNX(无过期,死锁)→SET key value EX NX(防死锁)→ Lua 脚本释放(防误删)→ 看门狗自动续期(防锁过期业务未完成)→ Redlock 算法(解决单点故障)。
35. 看门狗(Watchdog)机制是什么?
- 加锁后启动后台定时任务,在锁过期时间进行到 1/3 时自动续期,直到业务完成显式释放。避免因业务执行超时导致锁提前释放。
Python 核心实现:
def _watchdog_loop(self):whilenot self._stop: sleep(self.expire /3)ifrenew_script(keys=[lock_key],args=[holder, expire]): print("续期成功")36. Redlock 算法原理和流程?
在 N 个独立 Redis 节点(通常 5 个)上顺序尝试加锁,使用相同 key、随机值和 TTL。
若在多数节点(≥ N/2+1)加锁成功,且总耗时 < TTL,则获锁成功。
失败则向所有节点发送释放锁请求。锁的有效时间 = TTL - 耗时。
37. Redlock 安全吗?有什么争议?
网络安全专家认为,依赖系统时钟或遇到 GC 长停顿可能导致锁失效。
对于强一致性要求场景,推荐基于 ZooKeeper 或 etcd 的锁。
实际中 Redlock 仍被广泛使用,可配合 fencing token 提高安全性。
38. Python 有哪些 Redis 分布式锁库?
redlock-py:实现 Redlock 算法。redis-py内置lock方法:r.lock('mylock', timeout=10)提供上下文管理器,自带续期。
示例:
with r.lock('resource',timeout=10):# do something39. 分布式锁在秒杀场景中怎么用?
扣减库存前通过 Lua 脚本判断库存,无需额外锁(Lua 本身原子)。
但若涉及跨系统资源(如同时扣库存和生成订单),可用锁包裹整个流程。
40. 锁的可重入性需要吗?怎么实现?
- Redis 官方分布式锁不支持可重入,但可用
redisson(Java)或自定义:使用 ThreadLocal 计数,相同线程再次获取时递增计数,释放时递减到 0 才真正解锁。
六、消息队列与 Stream(5 问)
41. Redis 做消息队列有哪几种方式?优缺点?
List(BRPOP):简单队列,无 ACK,消息弹出即丢失,不能重复消费。
Pub/Sub:广播,消息不持久化,消费者离线丢失。
Stream(5.0+):消费者组、ACK、消息持久化、消息回溯,是真正的可靠队列。
42. Stream 的消费者组如何工作?
同组消费者共享消费进度,竞争消费消息。
使用
XREADGROUP读取新消息(>),消息处理后XACK确认。若消费者崩溃,消息仍在 pending 中,可通过
XCLAIM转移给其他消费者。
43. 消息积压怎么办?如何监控?
XINFO STREAM查看长度、pending 数。临时扩容消费者数量;设置
MAXLEN限制 Stream 长度防止无限膨胀。为无法处理的消息设计死信队列。
44. 如何保证消息不丢?
生产者:
XADD后即持久化(取决于 AOF/RDB 配置)。消费者:处理完再
XACK;加上定时任务扫描 pending 重试或转移。
45. Redis Stream 与 Kafka 的对比?
Kafka 吞吐量更高,支持海量堆积(磁盘),适合大数据管道。
Stream 轻量、零运维,适合中小型可靠消息场景,且复用现有 Redis。
七、性能优化与运维(5 问)
46. 如何发现慢查询?怎么优化?
SLOWLOG GET 10查看慢日志,设置slowlog-log-slower-than 1000(1ms)。优化:避免
KEYS、大 Key;使用SCAN替换;复杂查询用 Lua 合并命令;使用 Pipeline。
47. 大 Key 有什么危害?如何排查和优化?
危害:内存不均、迁移阻塞、删除时主线程卡顿。
排查:
redis-cli --bigkeys、MEMORY USAGE key、Python 扫描脚本。优化:拆分大 Hash/List,采用
UNLINK异步删除,分批删除。
48. 线上 Redis 变慢了,从哪些方面排查?
慢日志分析,看是否有慢命令。
检查 CPU/内存/网络使用率。
查看持久化是否影响(
BGSAVE对磁盘的压力)。连接数是否打满,是否存在热 key。
是否触发了内存淘汰(
evicted_keys增加)。
49. 如何做 Redis 性能基准测试?
redis-benchmark -n 100000 -c 50 -q -t set,get自定义 Python 脚本测试带业务逻辑的 Pipeline 和序列化开销。
50. Python 客户端连接池的注意事项?
避免每次请求新建连接,使用全局连接池单例。
max_connections设置合适(并发数 + 缓冲)。开启
health_check_interval和retry_on_timeout。在异步项目中使用
redis.asyncio客户端以利用事件循环。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !