Redis 面试八股文 30 道|深度详解版(傻子都能看懂)
🗺️ 知识图谱
mindmap
root((Redis))
数据结构
String SDS
List quicklist
Hash listpack 与 hashtable
Set intset
ZSet skiplist
Bitmap HyperLogLog GEO
持久化
RDB 快照
AOF 与重写
混合持久化
过期与淘汰
惰性删除
定期删除
八种淘汰策略
高可用
主从复制
哨兵 Sentinel
Cluster 与槽位
缓存问题
穿透 布隆过滤器
击穿 互斥锁
雪崩 过期打散
一致性 延迟双删
单线程模型
IO 多路复用
Redis 6 多线程 IO
Redis 面试八股文 30 道|深度详解版
写给准备面试的你:
这篇文章不讲废话,每个知识点都从「是什么 → 为什么 → 怎么用」三个层次讲透。
配有大量比喻和场景化解释,目标是让没有 Redis 基础的人也能看懂。
建议配合实际 Redis 操作练习,理解效果翻倍。
一、基础与数据结构(1-8)
第 1 题:Redis 和 Memcached 到底有什么区别?我该选哪个?
一句话结论
Memcached 是简单的 KV 缓存;Redis 是全能型数据结构服务器,支持持久化、主从复制、Lua 脚本、事务等。99% 的场景选 Redis。
深度解析
要从五个核心维度理解它们的差异:
① 数据结构丰富度
| 对比项 | Memcached | Redis |
|---|---|---|
| 数据类型 | 只支持 String | String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Geo、Stream |
| 单个 value 大小 | 最大 1MB | 最大 512MB(String 类型) |
| 复杂操作 | 需要客户端自己实现 | 服务端原生支持(如 ZADD、SINTER) |
场景化理解:
用 Memcached 做「排行榜」:客户端需要把整个排行榜取出来,在内存中排序,再写回去 → 网络开销大,并发有 Race Condition。
用 Redis 做「排行榜」:ZADD leaderboard 100 user1一条命令搞定,服务端直接维护有序集合。
② 持久化能力
- Memcached:纯内存,重启后数据全部丢失。
- Redis:支持 RDB 快照 和 AOF 日志 两种持久化方式,重启后可以恢复数据。
③ 主从复制
- Memcached:没有原生的主从复制,需要客户端自己实现。
- Redis:原生支持主从复制,可以实现读写分离、高可用(配合 Sentinel)。
④ 内存管理
| 对比项 | Memcached | Redis |
|---|---|---|
| 内存分配 | 固定大小 Chunk(有碎片问题) | 自己实现的内存分配器(jemalloc) |
| LRU 淘汰 | 支持 | 支持(多种淘汰策略) |
| 键过期 | 不支持 | 支持(EXPIRE 命令) |
⑤ 性能对比
- Memcached:多线程,CPU 核心数越多性能越好,纯 KV 场景下略优于 Redis。
- Redis:单线程(6.0 前),但内存操作极快,性能瓶颈通常在网络 I/O 而非 CPU。
结论:除非你在维护遗留系统,否则新项目无脑选 Redis。
面试加分回答
「Memcached 在纯 KV、多线程利用多核 CPU 的场景下有一定优势,但 Redis 的功能丰富度和生态(客户端库、运维工具、云服务商支持)远超 Memcached。另外,Redis 6.0 引入了多线程 I/O(注意:命令执行仍然是单线程的),在网络 I/O 密集型场景下性能提升显著,进一步缩小了与 Memcached 的差距。」
第 2 题:Redis 的线程模型是什么?为什么单线程还这么快?
一句话结论
Redis 的核心命令执行是单线程的(6.0 前);「快」的原因是:内存操作 + 非阻塞 I/O(多路复用)+ 单线程避免了锁竞争和上下文切换。
深度解析
Redis 真的是「单线程」吗?
需要分版本说清楚:
1 | |
很多人误解「Redis 是单线程」=「Redis 只用了一个 CPU 核心」,这是错的。Redis 的「单线程」特指命令执行(命令读写、数据结构操作)是单线程的。
为什么单线程还这么快?四个原因:
原因 1:内存操作,纳秒级延迟
1 | |
Redis 所有数据都在内存中,操作延迟是纳秒级的,单线程也足够快。
原因 2:I/O 多路复用(非阻塞)
Redis 使用了 epoll(Linux)/ kevent(BSD) 多路复用机制:
1 | |
原因 3:单线程避免了锁竞争和上下文切换
多线程的问题:
1 | |
Redis 的单线程模型完全没有这些问题。
原因 4:高效的数据结构
Redis 的每种数据结构都经过极致优化(后面有专题讲解),比如:
- String 用 SDS(简单动态字符串),O(1) 获取长度。
- Hash 用渐进式 rehash,避免一次性扩容卡顿。
- Sorted Set 用跳表 + 哈希表,范围查询 O(log N)。
面试加分回答
「Redis 6.0 引入的多线程 I/O 是一个重要考点。需要注意的是:多线程只用于网络 I/O(读取客户端请求、写回响应),命令执行仍然是单线程的。这样设计的好处是:既利用了多核 CPU 处理网络 I/O 的能力,又避免了多线程命令执行带来的锁竞争和复杂度。配置方式是设置
io-threads参数(建议设为 CPU 核心数的 1/2 到 1 倍)。」
第 3 题:Redis 的五种基本数据结构是什么?分别适用什么场景?
一句话结论
String(字符串)、Hash(哈希)、List(列表)、Set(集合)、ZSet(有序集合)。选对数据结构,功能实现事半功倍。
深度解析
① String(字符串)
1 | |
最基础的类型, value 最大 512MB。
常用命令:
1 | |
适用场景:
- 缓存热点数据(如用户信息、配置)
- 分布式计数器(
INCR/DECR) - 分布式锁(
SETNX) - 位图统计(
SETBIT/GETBIT,如用户签到)
② Hash(哈希)
1 | |
类似 Java 的 HashMap,适合存储对象。
常用命令:
1 | |
适用场景:
- 存储对象(比 String 更节省空间,不需要序列化整个对象)
- 购物车(
HSET cart:user_id product_id quantity) - 用户属性(年龄、昵称、积分等)
③ List(列表)
1 | |
有序、可重复、双向链表。
常用命令:
1 | |
适用场景:
- 消息队列(
LPUSH+BRPOP) - 时间线(
LPUSH+LTRIM实现固定长度列表) - 栈(
LPUSH+LPOP)或队列(LPUSH+RPOP)
④ Set(集合)
1 | |
无序、不可重复。
常用命令:
1 | |
适用场景:
- 去重(如用户已读消息 ID)
- 共同好友(
SINTER user:1:friends user:2:friends) - 抽奖(
SRANDMEMBER随机取元素) - 标签系统(给用户打标签)
⑤ Sorted Set / ZSet(有序集合)
1 | |
有序、不可重复、每个成员关联一个 score(分数),按 score 排序。
常用命令:
1 | |
适用场景:
- 排行榜(最经典场景,如游戏积分榜)
- 延迟队列(score 设为执行时间戳,
ZRANGEBYSCORE取到期任务) - 滑动窗口限流(score 设为时间戳,统计时间窗口内的请求数)
面试加分回答
「除了这五种基本类型,Redis 还有三种特殊类型经常被问到:Bitmap(位图,适合海量布尔统计,如用户签到,1 亿用户签到只需 12MB)、HyperLogLog(基数估算,适合 UV 统计,误差 0.81%,只需 12KB 内存)、Geo(地理位置,底层用 ZSet 实现,适合「附近的人」功能)。」
第 4 题:SDS(简单动态字符串)和 C 字符串相比有什么优势?
一句话结论
SDS 解决了 C 字符串的三大缺陷:获取长度 O(1)(C 字符串是 O(n))、二进制安全(C 字符串不能存
\0)、避免缓冲区溢出。
深度解析
C 字符串的问题:
1 | |
SDS 的结构:
1 | |
SDS 的五大优势:
| 优势 | C 字符串 | SDS |
|---|---|---|
| 获取长度 | O(n),需遍历 | O(1),直接读 len 字段 |
| 二进制安全 | ❌(遇 \0 截断) |
✅(用 len 判断结束,不管 \0) |
| 缓冲区溢出 | 可能(strcat 不检查长度) | 不会(先检查 alloc,不够就扩容) |
| 修改时内存分配 | 每次修改都可能 realloc | 空间预分配 + 惰性空间释放(见下文) |
| API 安全性 | 不安全 | 安全(所有 API 都做边界检查) |
空间预分配(减少内存分配次数):
1 | |
例:第一次
SET key "a"(len=1),SDS 实际分配 2 字节;
追加到 len=13 时,SDS 实际分配 26 字节;
这样连续追加 N 次,内存分配次数从 N 次降到 O(log N) 次。
惰性空间释放(避免缩短时的内存分配):
1 | |
面试加分回答
「SDS 的设计体现了 Redis 对性能的极致追求。另外,Redis 5.0 对 SDS 做了进一步优化:根据字符串长度动态选择
sdshdr5/sdshdr8/sdshdr16/sdshdr32/sdshdr64,用最小的 header 存储长度信息,进一步节省内存。这也是为什么 Redis 在小数据场景下内存利用率非常高的原因。」
第 5 题:Redis 的 Hash 数据结构是如何实现的?渐进式 rehash 是什么?
一句话结论
Redis 的 Hash 底层用「哈希表(dictht)」实现,通过渐进式 rehash 把扩容/缩容的代价分摊到每次增删改查操作中,避免一次性 rehash 导致长时间阻塞。
深度解析
Hash 的底层编码:
Redis 的 Hash 类型有两种底层编码:
1 | |
dictht(字典哈希表)的结构:
1 | |
哈希冲突的解决:链地址法
1 | |
渐进式 rehash(重点!):
普通哈希表扩容时,需要把旧表的所有元素重新计算哈希值,搬到新表:
1 | |
Redis 的渐进式 rehash 把这个工作分摊了:
1 | |
关键:在 rehash 期间,增删改查如何工作?
1 | |
面试加分回答
「渐进式 rehash 是 Redis 单线程模型下保证低延迟的关键设计之一。值得注意的是,在 rehash 期间,如果哈希表很大,即使分摊了,单次搬迁(搬一个桶的整个链表)也可能有延迟尖刺。Redis 7.0 对 rehash 做了进一步优化,每次搬迁的桶数量动态调整,避免大桶导致的延迟。」
第 6 题:跳表(Skip List)的原理是什么?为什么 ZSet 用跳表而不是红黑树?
一句话结论
跳表是在有序链表上建立「多层索引」的数据结构,查找/插入/删除都是 O(log N);ZSet 用跳表而不是红黑树,因为跳表实现更简单、范围查询更高效、支持无锁并发(理论上)。
深度解析
有序链表的困境:
1 | |
跳表的核心思想:多层索引
1 | |
查找过程演示(查找 25):
1 | |
插入过程(带随机层数):
1 | |
随机层数的设计保证了:第 k 层的节点数大约是第 k-1 层的 1/2 → 跳表高度是 O(log N)。
为什么 ZSet 用跳表而不是红黑树?
| 对比维度 | 红黑树 | 跳表 |
|---|---|---|
| 实现复杂度 | 很高(旋转、变色) | 简单(就是多层链表) |
| 范围查询 | O(log N + M),但需要中序遍历 | O(log N + M),沿着第 0 层链表扫就行,更简单 |
| 内存占用 | 每个节点存颜色位 | 每个节点存多层指针(稍多) |
| 并发支持 | 很难无锁化 | 理论上可以无锁化(CAS) |
| 调试难度 | 难(黑红交替规则复杂) | 易(就是链表) |
Redis 作者 antirez 的原话:
「跳表的实现比红黑树简单得多,而且范围查询的性能更好,对于 Redis 的使用场景来说,跳表是更好的选择。」
面试加分回答
「跳表在工程中其实比红黑树更常用,除了 Redis 的 ZSet,还有 LevelDB / RocksDB 的 MemTable 也用跳表。另外,Redis 的 ZSet 实际上是跳表 + 哈希表的组合:跳表按 score 有序存储,哈希表存储 member→score 的映射(O(1) 判断 member 是否存在、获取 score)。这样 ZADD、ZSCORE 是 O(1),ZRANGE 是 O(log N + M)。」
第 7 题:Redis 的持久化机制——RDB 和 AOF 分别是什么?
一句话结论
RDB = 定时快照(全量备份,恢复快,但会丢数据);AOF = 追加日志(每条写命令都记录,安全,但文件大、恢复慢)。生产环境推荐同时开启两者(Redis 4.0+ 支持混合持久化)。
深度解析
RDB(Redis DataBase)快照:
1 | |
触发方式:
1 | |
优点:
- 恢复速度快(直接把 RDB 文件加载到内存,O(N),N 是数据量)
- 文件紧凑(二进制压缩,适合做冷备份、灾难恢复)
- 对性能影响小(fork 子进程做,父进程不阻塞)
缺点:
- 会丢数据:两次快照之间的数据会丢失(如配了
save 900 1,宕机最多丢 15 分钟数据)
AOF(Append Only File)日志:
1 | |
刷盘策略(appendfsync 参数):
| 策略 | 说明 | 性能 | 安全性 |
|---|---|---|---|
always |
每条命令都 fsync 到磁盘 | 最差(每次写都要等磁盘) | 最好(最多丢 1 条命令) |
everysec(默认,推荐) |
每秒 fsync 一次 | 平衡 | 最多丢 1 秒数据 |
no |
由操作系统决定何时刷盘 | 最好 | 最差(宕机可能丢大量数据) |
AOF 文件重写(Rewrite):
AOF 文件会越来越大(如对一个 key 做了 100 次 INCR,AOF 中有 100 条命令)。
重写机制:fork 子进程,根据当前内存数据,生成一份最小化的 AOF 文件(如对上述 key 直接生成 SET key 100)。
1 | |
同时开启 RDB 和 AOF 时的启动恢复流程:
1 | |
面试加分回答
「Redis 4.0 引入了混合持久化(aof-use-rdb-preamble),AOF 重写时,先把当前内存数据以 RDB 格式写入 AOF 文件,再把重写期间的增量命令以 AOF 格式追加。这样重启时,先加载 RDB 部分(快),再重放 AOF 增量部分(少),兼顾了 RDB 的加载速度和 AOF 的数据安全性,生产环境强烈推荐开启。」
第 8 题:Redis 的内存淘汰策略有哪些?
一句话结论
内存达到 maxmemory 上限时,Redis 会根据配置的策略来淘汰键。生产环境最常用的是
allkeys-lru(所有键参与 LRU 淘汰)或volatile-lru(只淘汰设置了过期时间的键)。
深度解析
8 种淘汰策略:
| 策略 | 含义 | 适用场景 |
|---|---|---|
noeviction(默认) |
不淘汰,写满时返回错误 | 不能丢数据的场景 |
allkeys-lru |
所有键中,淘汰最久未使用的 | 生产环境最常用 |
volatile-lru |
只淘汰设置了过期时间的键中最久未使用的 | 兼顾缓存和持久化键 |
allkeys-random |
所有键中随机淘汰 | 访问分布均匀时可用 |
volatile-random |
只淘汰设置了过期时间的键中随机的 | - |
volatile-ttl |
只淘汰设置了过期时间的键中,TTL 最小(最快过期)的 | - |
allkeys-lfu(4.0+) |
所有键中,淘汰访问频率最低的 | 有「热点数据 + 冷数据」的场景 |
volatile-lfu(4.0+) |
只淘汰设置了过期时间的键中访问频率最低的 | - |
LRU 和 LFU 的区别:
1 | |
Redis 的 LRU 是近似 LRU(抽样实现):
1 | |
面试加分回答
「Redis 的 LRU 淘汰策略实际上是近似 LRU,通过随机采样来近似,而不是维护一个严格的 LRU 链表。这样做的好处是节省内存、避免性能开销。另外,
maxmemory-samples参数控制每次采样的数量(默认 5,越大越接近真实 LRU,但性能稍差)。在 Redis 7.0 中,LFU 的实现进一步优化了频率衰减算法,使得热点数据的识别更准确。」
二、持久化与内存管理(9-13)
第 9 题:AOF 重写机制的原理是什么?
一句话结论
AOF 重写 = fork 子进程,根据当前内存数据生成最小化的 AOF 文件,再追加重写期间的增量命令。重写期间不阻塞主进程。
深度解析
AOF 文件膨胀的问题:
1 | |
AOF 文件会越来越臃肿,不仅占用磁盘,重启恢复时也要重放大量命令,非常慢。
AOF 重写的原理:
1 | |
为什么 fork 子进程而不是主进程自己做?
主进程自己做会阻塞(重写是大 IO 操作)。
fork 子进程后,子进程拥有 fork 时刻的内存快照(COW),
主进程继续处理请求,完全不阻塞。
COW(Copy-On-Write)的巧妙之处:
1 | |
面试加分回答
「AOF 重写和 RDB 都用了 fork + COW 机制,这是 Redis 能在单线程下做后台重 IO 操作的核心技巧。但要注意:fork 瞬间会有停顿(需要拷贝页表),如果内存很大(如 10GB+),fork 的停顿可能达到几十毫秒。缓解方式:调小
vm.overcommit_memory = 1(允许超额分配内存),或使用 Redis 7.0 的aof-timestamp-enabled来监控 fork 耗时。」
第 10 题:RDB 和 AOF 的优缺点对比?混合持久化是什么?
一句话结论
RDB = 快照,恢复快但丢数据;AOF = 日志,安全但文件大恢复慢。混合持久化(Redis 4.0+)= RDB 快照 + AOF 增量,兼顾两者优点。
深度解析
RDB vs AOF 全面对比:
| 对比维度 | RDB | AOF |
|---|---|---|
| 文件大小 | 小(二进制压缩) | 大(文本命令) |
| 恢复速度 | 快(直接加载到内存) | 慢(需要逐条重放命令) |
| 数据安全性 | 可能丢数据(两次快照之间的数据) | everysec 最多丢 1 秒数据 |
| 对性能影响 | 小(fork 子进程,父进程不阻塞) | 中等(everysec 模式下后台 fsync) |
| 灾难恢复 | 方便(一个文件,适合冷备) | 不方便(文件大,且可能损坏) |
| 启动优先级 | AOF 开启时优先用 AOF 恢复 | 高(数据更完整) |
混合持久化(Redis 4.0+,强烈推荐):
1 | |
开启方式:
1 | |
面试加分回答
「混合持久化是 Redis 持久化的『终极方案』。在生产环境中,建议同时开启 RDB(用于冷备)和 AOF(用于恢复),并开启混合持久化。另外,Redis 的持久化文件(RDB/AOF)建议定期备份到对象存储(如 S3/OSS),防止整机故障导致数据无法恢复。」
第 11 题:Redis 的过期键删除策略是什么?
一句话结论
Redis 使用「惰性删除 + 定期删除」的组合策略。惰性删除 = 访问时才检查是否过期;定期删除 = 后台定时抽样检查并删除过期键。
深度解析
如果只用心惰性删除:
1 | |
如果只用定期删除:
1 | |
Redis 的组合策略(扬长避短):
策略 ①:惰性删除(Lazy Expire)
1 | |
访问时顺带检查,过期就删。
优点:CPU 友好(不主动扫描)。
缺点:过期 key 如果不被访问,永远占用内存。
策略 ②:定期删除(Active Expire)
1 | |
优点:能清理掉「永远不被访问」的过期 key。
缺点:是抽样删除,不是全量扫描,可能有残留。
Redis 6.0 之后的改进:惰性删除后台线程
1 | |
删除大 key(如一个 100 万元的 Set)时会阻塞主线程,
开启惰性删除后,把DEL操作放到后台线程执行,不阻塞主进程。
面试加分回答
「Redis 4.0 引入了
lazyfree-lazy-expire,把过期 key 的删除操作放到后台线程,解决了大 key 删除阻塞的问题。另外,Redis 的内存淘汰(maxmemory-policy)和过期删除是两套独立机制:过期删除是针对带 TTL 的 key;内存淘汰是针对所有 key(当内存达到上限时)。面试时经常有人混淆这两个概念。」
第 12 题:缓存雪崩、缓存穿透、缓存击穿分别是什么?如何解决?
一句话结论
缓存雪崩 = 大量 key 同时失效;缓存穿透 = 查询不存在的 key;缓存击穿 = 热点 key 失效瞬间大量请求打到 DB。三者都有成熟的解决方案。
深度解析
① 缓存雪崩(Cache Avalanche)
问题描述:
1 | |
解决方案:
1 | |
② 缓存穿透(Cache Penetration)
问题描述:
1 | |
解决方案:
1 | |
③ 缓存击穿(Cache Hotkey Breakdown)
问题描述:
1 | |
和「雪崩」的区别:雪崩是大量 key 同时失效;击穿是单个热点 key 失效。
解决方案:
1 | |
面试加分回答
「缓存穿透的解决方案,布隆过滤器虽然好,但有误判率(说存在时可能不存在)。在生产环境中,通常会布隆过滤器 + 缓存空值组合使用:布隆过滤器挡掉绝大多数不存在的请求,极少数误判的请求走缓存空值。另外,Redis 6.0 之后有 RedisBloom 官方模块,可以直接用,不需要自己实现布隆过滤器。」
第 13 题:缓存与数据库的一致性如何保证?
一句话结论
缓存与数据库一致性没有「完美方案」,只有「适合业务的方案」。最常用的是:先更新数据库,再删除缓存(Cache Aside Pattern)。
深度解析
先搞清楚:为什么会有不一致?
1 | |
Cache Aside Pattern(推荐方案):
1 | |
为什么是「删除缓存」而不是「更新缓存」?
1 | |
并发场景下的问题:
1 | |
发生概率:
上述场景要求:T1 读 DB 的耗时 > T2 写 DB + 删除缓存的耗时。
通常读 DB 比写 DB 慢(因为写可能只是更新内存中的数据,读要读磁盘),
所以这个场景发生概率很低。
如果一定要解决:
1 | |
面试加分回答
「缓存与 DB 一致性是一个经典难题,CAP 定理告诉我们不可能同时满足 CA。在工程实践中,最终一致性是可以接受的。另外,阿里开源的 Canal 可以监听 MySQL 的 binlog,当数据库变更时自动更新缓存,这是一种「先写 DB,异步刷新缓存」的模式,一致性更好,适合对一致性要求高的场景。」
三、高可用与分布式(14-21)
第 14 题:Redis 分布式锁的实现原理?有什么问题?
一句话结论
Redis 分布式锁用 SET key value NX EX timeout 实现;核心要解决:锁超时释放、误删别人的锁、锁的续期这三个问题。
深度解析
最基础的分布式锁:
1 | |
NX= 不存在时才设置(只有一个客户端能设置成功)EX 30= 30 秒后自动过期(防止死锁)
问题 ①:锁超时,业务还没执行完
1 | |
解决:给锁续期(看门狗机制)
1 | |
问题 ②:误删别人的锁
1 | |
解决:删除锁时验证 value(原子操作)
1 | |
每个客户端用唯一的 value(如 UUID)标识自己,
删除时先检查 value 是否是自己的,是才删除。
问题 ③:Redis 节点宕机,锁丢了
1 | |
解决:RedLock 算法(见下一题)
面试加分回答
「Redis 分布式锁在生产环境中不要自己实现,用成熟的库:Java 用 Redisson,Python 用 redis-py 的锁实现。这些库已经处理了锁续期、误删、可重入等复杂问题。另外,对于绝对不能出错的场景(如金融扣款),Redis 分布式锁不是最佳选择,应该用 ZooKeeper 或 etcd 的分布式锁(基于临时顺序节点,可靠性更高)。」
第 15 题:RedLock 算法的原理?有什么缺陷?
一句话结论
RedLock = 在 N 个独立 Redis 节点上依次获取锁,超过半数成功才认为获取锁成功。但 Martin Kleppmann 指出它有问题,建议用 fenced token 或直接用 ZooKeeper/etcd。
深度解析
RedLock 算法流程:
1 | |
RedLock 的优点:
即使有节点宕机,只要多数节点存活,锁服务就可用。
比单节点 Redis 锁可靠性高。
RedLock 的缺陷(Martin Kleppmann 批评):
缺陷 ①:系统时钟漂移问题
1 | |
缺陷 ②:GC 停顿导致锁过期
1 | |
RedLock 作者的反驳(antirez):
缺陷 ①:系统时钟跳跃是运维问题,应该避免(用合理的 NTP 配置)。
缺陷 ②:GC 停顿是客户端的问题,应该在客户端用「锁续期」解决。
更可靠的替代方案:
1 | |
面试加分回答
「RedLock 的争议是分布式系统领域的一个经典案例。结论是:如果能接受偶发的锁失效(如秒杀场景),RedLock 可用;如果绝对不能接受(如金融扣款),应该用 ZooKeeper/etcd,或者在业务层用fenced token(栅栏令牌)**机制:每次写存储时带上一个单调递增的 token,存储层拒绝 token 小于当前最大 token 的写请求,这样即使锁失效,旧客户端也无法成功写入。」
第 16 题:Redis 事务的实现原理?WATCH 命令是什么?
一句话结论
Redis 事务 = MULTI(开启) + 命令入队 + EXEC(提交)。WATCH 命令提供乐观锁,在 EXEC 前监控 key,如果被修改则事务失败。但 Redis 事务不支持回滚。
深度解析
Redis 事务的基本用法:
1 | |
事务中的命令不会被其他客户端的命令打断(序列化执行)。
WATCH 命令(乐观锁):
1 | |
WATCH的工作原理:在EXEC时,检查被 WATCH 的 key 的version(修改次数),
如果 version 变了(被其他客户端修改了),拒绝执行事务。
Redis 事务不支持回滚!
1 | |
Redis 事务 vs 关系型数据库事务:
| 对比项 | Redis 事务 | MySQL 事务 |
|---|---|---|
| 原子性 | ✅ 命令要么都执行,要么都不执行(但不支持回滚) | ✅ 支持回滚(undo log) |
| 一致性 | 由应用层保证 | ✅ 由数据库保证 |
| 隔离性 | ✅ 事务中的命令不会被其他客户端打断 | ✅ 支持多种隔离级别 |
| 持久性 | 取决于持久化配置 | ✅ 由 WAL 机制保证 |
面试加分回答
「Redis 的事务能力比较弱,如果需要复杂的事务逻辑(如多条命令的原子执行 + 回滚),应该用 Lua 脚本。
EVAL script numkeys key [key ...] [arg ...]可以执行一段 Lua 脚本,Redis 保证整个 Lua 脚本的原子执行(执行期间不会执行其他客户端的命令)。这是实现「先读后写」原子操作的最佳方式。」
第 17 题:Redis Pipeline 的原理?和 MGET/MSET 的区别?
一句话结论
Pipeline = 客户端批量发送命令,减少 RTT(往返时延);MGET/MSET = 服务端批量处理命令。Pipeline 是客户端优化,MGET 是服务端优化。
深度解析
没有 Pipeline 的世界:
1 | |
Pipeline:
1 | |
注意:Pipeline 不是原子操作!命令是一条条执行的,只是网络传输合并了。
MGET / MSET:
1 | |
Pipeline vs MGET/MSET:
| 对比项 | Pipeline | MGET/MSET |
|---|---|---|
| 实现层 | 客户端(批量发送) | 服务端(批量处理) |
| 原子性 | ❌ 不保证 | ✅ MSET 保证原子性 |
| 适用场景 | 任意多条命令批量执行 | 批量 GET/SET 同名命令 |
| 性能 | 减少 RTT | 减少 RTT + 服务端优化 |
面试加分回答
「Pipeline 和 Lua 脚本的区别是一个高频追问。Pipeline 只是减少网络 RTT,命令在服务端仍然是逐条执行的;Lua 脚本是整个脚本原子执行(服务端保证不被打断)。所以如果需要在「先读后写」的场景下保证原子性,应该用 Lua 脚本而不是 Pipeline。」
第 18 题:Redis 发布订阅(Pub/Sub)的原理?有什么缺陷?
一句话结论
Pub/Sub = 发布者发消息到频道,订阅者接收消息。但不保证消息可靠性(订阅者下线会丢消息),生产环境建议用 Stream 或专业 MQ(Kafka/RocketMQ)。
深度解析
基本用法:
1 | |
Pub/Sub 的缺陷:
1 | |
Redis Stream(Redis 5.0+,推荐替代 Pub/Sub):
1 | |
Stream 支持:消息持久化、消费者组、消息 ACK、Pending 消息重试。
是 Redis 5.0 之后做消息队列的首选。
面试加分回答
「Redis 的 Pub/Sub 适合「即时消息推送」(如聊天室、实时通知),但不适合对可靠性要求高的场景。如果需要消息队列,Redis 有三种方案:① List(LPUSH + BRPOP,简单但不支持 ACK);② Pub/Sub(即时但不可靠);③ Stream(5.0+,功能最完整,类似简化版 Kafka)。生产环境中,如果消息量很大或对可靠性要求很高,建议直接用 Kafka 或 RocketMQ,不要用 Redis 做消息队列。」
第 19 题:Redis 主从复制的原理?
一句话结论
主从复制 = 从节点连接主节点,全量同步(RDB 快照)+ 增量同步(命令传播)。2.8+ 使用 PSYNC 命令,支持断线后部分重同步。
深度解析
完整主从复制流程:
1 | |
PSYNC 命令(Redis 2.8+):
1 | |
复制偏移量(Replication Offset):
1 | |
面试加分回答
「主从复制的延迟(主从不一致)是一个常见问题。Redis 的复制是异步的,Master 执行完写命令就返回客户端了,然后才把命令发送给 Slave,所以 Slave 的数据一定是有延迟的。如果业务需要『写完立即读到』,要么读主库,要么用 WAIT 命令(阻塞等待指定数量的 Slave 确认收到写命令)。另外,Redis 的复制积压缓冲区(replication backlog)的大小是可以配置的,如果 Slave 断线时间较长,积压缓冲区的数据被覆盖了,就会触发全量同步,所以
repl-backlog-size要配置得足够大。」
第 20 题:Redis Sentinel(哨兵)的原理?
一句话结论
Sentinel = 监控 Master 是否宕机,自动故障转移(选一个新 Master),并通知客户端新的 Master 地址。Sentinel 本身也要集群部署(防止单点故障)。
深度解析
Sentinel 的三个作用:
1 | |
主观下线(SDOWN)vs 客观下线(ODOWN):
1 | |
Sentinel 集群的 Leader 选举(Raft 协议简化版):
1 | |
面试加分回答
「Sentinel 的故障转移需要时间(通常 30 秒~1 分钟),期间写请求会失败。如果对可用性要求很高,可以考虑 Redis Cluster(去中心化,每个节点都能处理请求,故障转移更快)。另外,Sentinel 模式下,客户端需要用 Sentinel 连接池(如 Java 的 JedisSentinelPool),而不是写死 Master 地址,这样 Sentinel 切换 Master 后,客户端才能自动感知。」
第 21 题:Redis Cluster(集群)的原理?
一句话结论
Redis Cluster = 去中心化的分布式方案,数据分片(16384 个槽),每个节点负责一部分槽。支持自动故障转移,无代理(客户端直接连对应节点)。
深度解析
数据分片(Sharding):
1 | |
客户端路由:
1 | |
主从复制(每个主节点可以有从节点):
1 | |
ASK 和 MOVED 的区别:
1 | |
面试加分回答
「Redis Cluster 的 ASK 重定向和 MOVED 重定向的区别是一个高频追问。简单来说:MOVED 是『槽已经迁移完了』,客户端要更新路由表;ASK 是『槽正在迁移中』,客户端临时重定向,不更新路由表。另外,Redis Cluster 不支持多 key 跨槽操作(如 MSET key1 key2,如果 key1 和 key2 在不同槽,会报错),解决方式是使用 Hash Tag:
{user123}name和{user123}age会用大括号内的内容计算槽,保证在同一槽。」
四、实战与优化(22-30)
第 22 题:热 key 问题如何解决?
一句话结论
热 key = 某个 key 访问量极高,单节点扛不住。解决方案:本地缓存 + 备份热 key(分散到多个节点)。
深度解析
问题场景:
1 | |
解决方案 ①:本地缓存(一级缓存)
1 | |
解决方案 ②:热 key 备份(分散到多个节点)
1 | |
如何发现热 key?
1 | |
面试加分回答
「热 key 的发现和解决是 Redis 运维的核心能力。在生产环境中,通常会部署 Redis 监控(如 Prometheus + Grafana),监控每个 key 的 QPS。另外,阿里云和腾讯云的 Redis 服务都提供了热 key 自动发现功能,不需要自己实现。对于读多写少的热 key,本地缓存是最有效的方案;对于读写都频繁的热 key,需要用热 key 备份方案。」
第 23 题:大 key 问题如何解决?
一句话结论
大 key = 某个 key 的 value 很大(如 1 个 String 有 10MB,或 1 个 Set 有 100 万元素)。会导致:阻塞请求、网络阻塞、迁移困难。解决:拆分大 key。
深度解析
大 key 的危害:
1 | |
如何发现大 key?
1 | |
大 key 的拆分方案:
1 | |
安全删除大 key:
1 | |
面试加分回答
「Redis 4.0 引入了 lazyfree(惰性删除),把大 key 的删除放到后台线程执行,不阻塞主进程。配置方式:
lazyfree-lazy-eviction yes、lazyfree-lazy-expire yes、lazyfree-lazy-server-del yes。在生产环境中,这些参数一定要开启,否则删除大 key 可能导致整个 Redis 实例卡顿几秒到几十秒。」
第 24 题:Redis 慢查询如何排查?
一句话结论
*Redis 慢查询 = 执行时间超过阈值的命令。通过 slowlog get 查看,重点排查:大 key 操作、复杂度高的命令(KEYS 、HGETALL 等)、网络延迟。
深度解析
开启慢查询日志:
1 | |
慢日志输出示例:
1 | |
常见慢查询原因排行:
1 | |
面试加分回答
「
KEYS *是 Redis 运维的禁忌命令,会在生产环境中把 Redis 打挂。替代方案:用 SCAN 命令(增量遍历,不阻塞)。另外,Redis 6.0 引入了 ACL(访问控制),可以禁止某些用户执行危险命令(如KEYS、FLUSHALL),这是生产环境安全加固的重要手段。」
第 25 题:Redis 6.0 多线程详解
一句话结论
Redis 6.0 的多线程只用于网络 I/O(读取请求、写回响应),命令执行仍然是单线程的。性能提升约 2~3 倍(网络 I/O 密集型场景)。
深度解析
为什么 Redis 6.0 要引入多线程 I/O?
1 | |
Redis 6.0 多线程模型:
1 | |
如何开启多线程 I/O?
1 | |
注意:
io-threads建议设为 CPU 核心数的 1/2 ~ 1 倍,不要超过 CPU 核心数。
面试加分回答
「Redis 6.0 的多线程 I/O 是一个经常被误解的特性。需要明确:命令执行仍然是单线程的,多线程只用于网络 I/O 的读写。这样设计的好处是:既利用了多核 CPU 处理网络 I/O 的能力,又避免了多线程命令执行带来的锁竞争和复杂度。另外,Redis 7.0 进一步优化了多线程 I/O,支持多线程同时处理读写(6.0 只有写是多线的,读是单线的),性能更好。」
第 26 题:Redis Stream 数据结构详解
一句话结论
Stream = Redis 的持久化消息队列,支持消费者组、消息 ACK、Pending 消息重试。是 Pub/Sub 的升级版,适合做消息队列。
深度解析
Stream 的核心概念:
1 | |
基本用法:
1 | |
消费者组(Consumer Group):
1 | |
Stream vs Kafka:
| 对比项 | Redis Stream | Kafka |
|---|---|---|
| 吞吐量 | 较低(约几万 QPS) | 很高(百万 QPS) |
| 消息持久化 | 支持(AOF/RDB) | 支持(多副本) |
| 消费者组 | ✅ 支持 | ✅ 支持 |
| 消息回溯 | ✅ 支持(指定 ID 读取) | ✅ 支持 |
| 适用场景 | 轻量级消息队列 | 大数据量消息队列 |
面试加分回答
「Redis Stream 是 Redis 5.0 引入的重要特性,让 Redis 终于有了一个可靠的消息队列实现。但在生产环境中,是否用 Stream 做消息队列,取决于消息量:如果 QPS < 10 万,Stream 完全够用;如果 QPS > 10 万,建议用 Kafka 或 RocketMQ。另外,Stream 的
XCLAIM命令可以实现消息重试(把 Pending 超时的消息分配给其他消费者),这是实现「至少一次投递」的关键。」
第 27 题:Redis 7.0 新特性
一句话结论
Redis 7.0 的重要新特性:Function(替代 Lua 脚本)、ACL 增强、RDB 频率可配置、性能优化(Hash/Set 编码优化)。
深度解析
新特性 ①:Function(函数):
1 | |
新特性 ②:ACL 增强:
1 | |
新特性 ③:RDB 保存频率可配置:
1 | |
面试加分回答
「Redis 7.0 的 Function 是一个比较冷门但很有用的特性,它解决了 Lua 脚本在持久化和复制场景下的问题。另外,Redis 7.2(最新版本)还引入了 Vector Similarity Search(向量相似度搜索),可以直接在 Redis 中做向量检索(类似 Milvus),这对于 AI 应用(如 RAG)非常有用,不需要单独部署向量数据库。」
第 28 题:Redis 内存优化技巧
一句话结论
内存优化 = 选对数据结构 + 控制 key/value 大小 + 开启内存淘汰策略 + 定期清理大 key/过期 key。
深度解析
技巧 ①:使用正确的数据结构
1 | |
技巧 ②:控制 key 的长度
1 | |
技巧 ③:开启内存淘汰策略
1 | |
技巧 ④:使用整数集合(IntSet)和压缩列表(ZipList)
1 | |
面试加分回答
「Redis 的内存优化有一个很实用的工具:MEMORY DOCTOR(Redis 7.0+),可以自动分析内存使用情况并给出优化建议。另外,对于海量小 key 的场景(如 1 亿个 key),Redis 的字典哈希表本身的 overhead 就很大(每个 key 至少有 64 字节的元数据),这时应该考虑分片(用 Hash Tag 把相关数据放到同一个槽)或者用 Redis Module(如 ReJSON,存储 JSON 比 String 省内存得多)。」
第 29 题:Redis 安全加固建议
一句话结论
Redis 安全 = 禁止 ROOT 运行 + 设置密码 + 重命名/禁用危险命令 + 配置 ACL + 不要暴露到公网。
深度解析
安全加固清单:
1 | |
面试加分回答
「Redis 未授权访问漏洞是一个经典的安全问题(CVE-2018-12326)。如果 Redis 没有设置密码,且监听在 0.0.0.0,攻击者可以直接连接 Redis,写入 SSH 公钥,然后 SSH 登录服务器!所以生产环境的 Redis 一定要设置密码 + 禁止 ROOT 运行 + 不要暴露到公网。另外,云服务商(阿里云/腾讯云)的 Redis 服务都内置了这些安全加固,自己部署时更需要小心。」
第 30 题:如何设计一个高并发的秒杀系统(Redis 为核心)?
一句话结论
秒杀系统 = Redis 做库存预扣减(原子操作)+ 消息队列异步下单 + 数据库最终扣减。核心是:把「写数据库」变成「写 Redis + 异步批处理」。
深度解析
秒杀的核心挑战:
1 | |
秒杀系统架构:
1 | |
核心代码(Redis 原子扣减库存):
1 | |
完整秒杀流程:
1 | |
面试加分回答
「秒杀系统的核心是削峰填谷:用 Redis 抗瞬时高并发,用消息队列把高峰请求平滑化,用数据库做最终一致性扣减。另外,对于超热门秒杀(如 100 万 QPS),单台 Redis 也扛不住,这时需要用 Redis Cluster(把库存分散到多个节点,每个节点负责一部分库存)。还有一个细节:秒杀前要预热(把库存提前加载到 Redis),否则第一波请求会全部打到数据库。」
第 41 题:Redis 做消息队列有哪几种方案?分别适合什么场景?
一句话结论
Redis 做消息队列有 3 种方案:① List(简单但无 ACK)② Pub/Sub(快但不持久化)③ Stream(5.0+,功能最完整)。优先级:Stream > List > Pub/Sub。
深度解析
方案 ①:List(LPUSH + BRPOP)
1 | |
优点:简单,自带持久化(AOF/RDB)
缺点:❌ 无 ACK 机制(消费者挂了,消息就丢了)、❌ 不支持消费者组(无法并行消费)
适用场景:简单的任务队列(如发送邮件、生成报表),对可靠性要求不高
方案 ②:Pub/Sub(发布订阅)
1 | |
优点:实时推送,支持模式订阅(PSUBSCRIBE news.*)
缺点:❌ 消息不持久化(订阅者下线期间消息全部丢失)、❌ 无 ACK 机制
适用场景:即时消息推送(如聊天室、实时通知),允许丢消息
方案 ③:Stream(Redis 5.0+,推荐)
1 | |
优点:✅ 消息持久化、✅ ACK 机制、✅ 消费者组、✅ 消息回溯
缺点:复杂度较高
适用场景:对可靠性要求高的消息队列(如订单处理),QPS < 10 万时可以用
3 种方案对比:
| 对比项 | List | Pub/Sub | Stream |
|---|---|---|---|
| 消息持久化 | ✅ | ❌ | ✅ |
| ACK 机制 | ❌ | ❌ | ✅ |
| 消费者组 | ❌ | ❌ | ✅ |
| 消息回溯 | ❌ | ❌ | ✅ |
| 复杂度 | 低 | 低 | 中 |
| 推荐指数 | ⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ |
面试加分回答
「Redis 做消息队列的最大问题是无法保证严格顺序(List 可以,但 Stream 的消费者组模式下,不同消费者的消息顺序无法保证)。另外,如果消息量很大(QPS > 10 万),或者对延迟极其敏感,建议直接用 Kafka 或 RocketMQ,不要用 Redis。Redis 做消息队列只适合轻量级场景(如站内信、异步审计日志)。」
第 42 题:Redis 热点 Key 的发现和处理方案详解
一句话结论
热点 Key = 访问频率远超其他 Key 的 Key。发现方式:monitor 命令(不推荐)、–hotkeys 参数(推荐)、业务日志;处理方案:本地缓存 + Key 备份 + 读写分离。
深度解析
如何发现热点 Key?
| 方法 | 命令/工具 | 优点 | 缺点 |
|---|---|---|---|
| redis-cli –hotkeys | redis-cli --hotkeys -i 0.1 |
官方工具,准确 | 需要开启 LFU 策略 |
| monitor + 分析 | redis-cli monitor | sort | uniq -c |
简单 | 影响性能,生产环境禁用 |
| 业务层统计 | 在代码中记录每个 Key 的访问次数 | 最准确 | 需要改代码 |
| Proxy 层统计 | Codis/Twemproxy 自带热点 Key 统计 | 无侵入 | 需要部署 Proxy |
| 云服务商 | 阿里云/腾讯云 Redis 控制台 | 最方便 | 需要花钱 |
处理方案 ①:本地缓存(一级缓存)
1 | |
适用场景:读多写少的热点 Key(如首页推荐、商品详情)
处理方案 ②:Key 备份(分散热点)
1 | |
注意:写操作需要更新所有备份,或者用「本地缓存 + 短过期」方案
处理方案 ③:读写分离
1 | |
适用场景:读多写少,且热点 Key 数量不多
面试加分回答
「热点 Key 的处理是一个系统性问题,不是单靠 Redis 就能解决的。在生产环境中,通常会多层防护:① 业务层限流(每个用户最多 N 次/秒);② 本地缓存(Caffeine);③ Redis 读写分离;④ 如果是电商秒杀场景,还会用 CDN 缓存静态页面 + 库存分散到多个 Redis Key(分桶扣减)。另外,Redis 7.0 引入了 hashscript 功能,可以在服务端执行 Lua 脚本,减少网络往返,对热点 Key 场景有一定缓解作用。」
第 43 题:Redis 和本地缓存(如 Caffeine)如何搭配使用?
一句话结论
多级缓存 = 本地缓存(Caffeine/Guava)做一级缓存,Redis 做二级缓存。读取时先查本地,未命中再查 Redis;写入时先写 DB,再删除 Redis 缓存,最后清理本地缓存。
深度解析
为什么要多级缓存?
1 | |
多级缓存架构:
1 | |
Java 代码示例(Spring Boot + Caffeine):
1 | |
面试加分回答
「多级缓存的一致性问题是一个难点。如果数据库更新了,但本地缓存还没过期,就会读到旧数据。解决方案:① 缩短本地缓存的过期时间(如 1~5 分钟);② 订阅 Redis 的过期/删除事件(Redis Keyspace Notifications),当 Redis 缓存被删除时,通知所有本地节点删除本地缓存;③ 用 消息队列(如 RocketMQ)广播缓存失效消息。另外,对于分布式本地缓存(多个应用节点),可以考虑用 Redis Pub/Sub 广播缓存失效消息。」
第 44 题:Redis 的过期键删除策略 vs 内存淘汰策略,有什么区别?
一句话结论
过期键删除策略 = 处理「带 TTL 的过期 Key」(惰性删除 + 定期删除);内存淘汰策略 = 处理「内存达到上限时,删哪些 Key」(LRU/LFU/Random 等)。两者是独立的机制。
深度解析
过期键删除策略(针对带 TTL 的 Key):
1 | |
内存淘汰策略(针对所有 Key):
1 | |
两者的关系:
1 | |
面试加分回答
「这两个概念经常被混淆。记住一个简单的判断方法:过期键删除策略只管「过期的 Key」;内存淘汰策略管「所有 Key」(当内存不够时)。 另外,Redis 的
maxmemory-policy配置中的volatile-*策略(如volatile-lru),只淘汰「设置了过期时间的 Key」,如果所有 Key 都没设置过期时间,Redis 会报错(和noeviction一样)!这是生产环境的一个大坑,一定要确保所有 Key 都设置了过期时间,或者改用allkeys-*策略。」
第 45 题:Redis 事务 vs Lua 脚本,该用哪个?
一句话结论
Redis 事务(MULTI/EXEC)很弱(不支持回滚、多条命令非原子);Lua 脚本(EVAL)是真正的原子操作。需要「先读后写」的原子性时,用 Lua 脚本。
深度解析
Redis 事务的问题:
1 | |
Lua 脚本的优势:
1 | |
Lua 脚本示例(秒杀库存扣减):
1 | |
Redis 事务 vs Lua 脚本对比:
| 对比项 | Redis 事务 | Lua 脚本 |
|---|---|---|
| 原子性 | ✅ 不会被打断,但不支持回滚 | ✅ 真正原子执行 |
| 复杂度 | 低(只能打包命令) | 高(支持逻辑控制) |
| 网络 RTT | 多次(MULTI/命令/EXEC) | 一次(EVAL) |
| 适用场景 | 简单批量命令 | 先读后写、复杂逻辑 |
面试加分回答
「Lua 脚本虽然强大,但有执行时长限制(默认 5 秒,由
lua-time-limit配置)。如果 Lua 脚本执行时间过长,会阻塞 Redis 主线程,导致其他请求超时。所以 Lua 脚本中不要有长时间循环,尽量用redis.call()而不是redis.pcall()(pcall会捕获错误,但性能稍差)。另外,Redis 7.0 引入了 Function(函数),可以替代 Lua 脚本,支持持久化和复制,是未来的方向。」
第 46 题:Redis 常见性能问题与优化方案
一句话结论
Redis 性能问题主要由:大 Key、热 Key、慢查询命令、内存碎片、网络延迟引起。优化方案:拆分大 Key、本地缓存抗热 Key、用 SCAN 替代 KEYS、开启内存碎片整理。
深度解析
性能问题 ①:大 Key(超过 10KB 的 String,或超过 5000 个元素的集合)
1 | |
性能问题 ②:热 Key(访问频率极高的 Key)
1 | |
性能问题 ③:慢查询命令(O(N) 复杂度的命令)
1 | |
性能问题 ④:内存碎片
1 | |
面试加分回答
「Redis 性能调优的第一步是找到瓶颈。用
redis-cli --latency检查延迟,用redis-cli --bigkeys和--hotkeys找大 Key 和热 Key,用SLOWLOG GET找慢查询。另外,Redis 的 hz 参数(默认 10)控制后台任务的执行频率(如定期删除过期 Key、后台重写 AOF),如果 Redis 的 CPU 占用不高,可以适当调大hz(如 20~50),让后台任务更积极,减少内存碎片和过期 Key 堆积。」
第 47 题:Redis 集群方案选型:Sentinel vs Cluster vs Codis
一句话结论
Sentinel = 主从高可用(故障自动切换),客户端需要支持 Sentinel;Cluster = 分布式(数据分片 + 高可用),无代理;Codis = 代理模式分布式(类似于 Codis 是中国公司开源的)。优先级:Cluster > Sentinel > Codis(Codis 已停止维护)。
深度解析
Sentinel(哨兵模式):
1 | |
Cluster(集群模式,推荐):
1 | |
Codis(代理模式,已停止维护):
1 | |
3 种方案对比:
| 对比项 | Sentinel | Cluster | Codis |
|---|---|---|---|
| 分布式 | ❌ | ✅ | ✅ |
| 高可用 | ✅ | ✅ | ✅ |
| 无代理 | ✅ | ✅ | ❌ |
| 客户端支持 | 需要支持 Sentinel | 需要支持 Cluster | 不需要(连 Proxy) |
| 维护状态 | ✅ 官方维护 | ✅ 官方维护 | ❌ 已停止维护 |
| 推荐指数 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐ |
面试加分回答
「Redis Cluster 的槽迁移是一个复杂的话题。在迁移槽的过程中,会有 ASK 和 MOVED 重定向,客户端需要正确处理这两种重定向。另外,Redis Cluster 的跨槽操作限制(如 MSET、MGET 的 Key 必须在同一槽),可以通过 Hash Tag 解决:
{user123}name和{user123}age会用大括号内的内容计算槽,保证在同一槽。生产环境中,建议用 Redis Cluster 客户端(如 Java 的 JedisCluster、Lettuce),它们已经处理了重定向、重试等复杂逻辑。」
第 48 题:Redis 6.0 之后的新特性总结(面试高频)
一句话结论
Redis 6.0:多线程 I/O、ACL 访问控制、RESP3 协议;Redis 7.0:Function 替代 Lua 脚本、Hash/Set 编码优化;Redis 7.2:向量相似度搜索(Vector Similarity Search)。
深度解析
Redis 6.0 新特性:
| 特性 | 说明 | 面试重要性 |
|---|---|---|
| 多线程 I/O | 网络 I/O 多线程,命令执行仍单线程 | ⭐⭐⭐⭐⭐ |
| ACL(访问控制) | 用户权限管理,限制命令和 Key | ⭐⭐⭐ |
| RESP3 协议 | 新的通信协议,支持更多数据类型 | ⭐⭐ |
| Client-side caching | 客户端缓存(类似本地缓存) | ⭐⭐⭐ |
Redis 7.0 新特性:
| 特性 | 说明 | 面试重要性 |
|---|---|---|
| Function | 替代 Lua 脚本,支持持久化 | ⭐⭐⭐ |
| Hash/Set 编码优化 | 小数据量时用 listpack 替代 ziplist | ⭐⭐ |
| RDB 保存频率动态配置 | CONFIG SET save 不需要重启 | ⭐⭐ |
| AOF 文件组织优化 | AOF 重写时,按类型分组命令 | ⭐ |
Redis 7.2 新特性(最新):
| 特性 | 说明 | 面试重要性 |
|---|---|---|
| Vector Similarity Search | 向量相似度搜索(支持 AI 应用) | ⭐⭐⭐⭐ |
| Hash Field Expiration | Hash 的单个字段支持过期时间 | ⭐⭐⭐ |
面试加分回答
「Redis 7.2 的 Vector Similarity Search(VSS) 是一个非常有用的新特性,让 Redis 可以直接做向量检索(类似 Milvus、Pinecone)。这对于 AI 应用(如 RAG、语义搜索)非常有用,不需要单独部署向量数据库。用法示例:
FT.CREATE idx ON HASH PREFIX 1 doc: SCHEMA embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 768(创建向量索引),FT.SEARCH idx "*=>[KNN 10 @embedding $query_vec]"(向量相似度搜索)。另外,Redis 的 VSS 是基于 HNSW 算法(Hierarchical Navigable Small World),查询速度快,但构建索引稍慢。」
第 49 题:Redis 使用规范与最佳实践
一句话结论
*Redis 使用规范:Key 设计要简洁有意义、避免大 Key 和热 Key、禁止危险命令(KEYS 、FLUSHALL)、设置过期时间、用 Pipeline 批量操作、开启 AOF 持久化。
深度解析
规范 ①:Key 设计规范
1 | |
规范 ②:Value 设计规范
1 | |
规范 ③:命令使用规范
1 | |
规范 ④:运维规范
1 | |
面试加分回答
「Redis 的使用规范是生产环境稳定性的保障。除了上述规范,还有一个重要的点:监控。生产环境一定要监控 Redis 的
used_memory(内存使用)、ops_per_sec(QPS)、connected_clients(连接数)、rejected_connections(拒绝连接数)、expired_keys(过期 Key 数)、evicted_keys(淘汰 Key 数)、keyspace_hits/misses(缓存命中率)。可以用 Prometheus + Grafana 监控,也可以用云服务商提供的监控(阿里云/腾讯云)。另外,Redis 的 mem_fragmentation_ratio(内存碎片率)也是一个重要指标,如果 > 1.5,说明碎片严重,需要开启内存碎片整理。」
第 50 题:Redis 面试题终极总结与复习路线
一句话结论
Redis 面试的核心:数据结构与底层实现、持久化、内存管理、缓存异常、分布式锁、高可用(主从/Sentinel/Cluster)、性能优化。按照「基础 → 原理 → 实战」的顺序复习。
深度解析
Redis 面试知识图谱:
1 | |
复习路线(按优先级):
1 | |
面试加分回答
「Redis 的八股文虽然多,但核心就那几个:数据结构底层、持久化、缓存异常、分布式锁、Cluster。其他的知识点(如 7.0 新特性、Vector Similarity Search)是加分项,不是必会项。另外,Redis 的官方文档(redis.io)非常详细,建议把 Redis Design Patterns(redis.io/docs/reference/patterns)这一章看完,里面讲了 Redis 的常见使用模式和最佳实践,对面试和实战都很有帮助。最后,如果你能结合自己的项目经验讲 Redis(如『我们项目的缓存架构是…』、『我们遇到过热点 Key 问题,解决方案是…』),面试效果会好得多。」
总结:Redis 面试复习 Checklist
| 模块 | 题号 | 核心考点 |
|---|---|---|
| 基础与数据结构 | 1-8 | Memcached vs Redis、线程模型、5 种数据结构、SDS、Hash 实现、跳表、持久化、内存淘汰 |
| 持久化与内存 | 9-13 | AOF 重写、RDB vs AOF、过期策略、缓存雪崩/穿透/击穿、缓存一致性 |
| 高可用与分布式 | 14-21 | 分布式锁、RedLock、事务、Pipeline、Pub/Sub、主从复制、Sentinel、Cluster |
| 实战与优化 | 22-30 | 热 key、大 key、慢查询、6.0 多线程、Stream、7.0 新特性、内存优化、安全、秒杀系统 |
最后的建议:
Redis 的八股文相对 MySQL 来说更「实用」,几乎每道题都能直接用到生产环境中。
建议把每道题都自己动手操作一遍(搭一个本地 Redis,试一下哨兵模式、Cluster 模式),
这样理解会深刻得多。
祝你面试顺利!🚀
“””
append_redis.py - Redis 补充题 31-40,追加到文件末尾
“””
import re
file_path = r”D:\Blog\my-blog\source_posts\2026-06-07-redis-interview-deep.md”
supplement = r
补充篇(续):高频遗漏题(39-50题)
第 39 题:Redis 后台线程(BIO)详细解析
一句话结论
Redis 有 3 个后台线程(Redis 6.0+),专门处理耗时的异步操作(如关闭大文件、AOF 刷盘、大 Key 异步删除),避免阻塞主线程。
深度解析
为什么需要后台线程?
1 | |
Redis 的 3 个后台线程(BIO:Background IO):
1 | |
如何开启后台线程?
1 | |
面试加分回答
「Redis 的后台线程是 Redis 6.0 之后才完善的。在 Redis 4.0 之前,删除大 Key 会阻塞主线程好几秒甚至几十秒,这是生产环境 Redis 卡顿的常见原因。Redis 4.0 引入了
UNLINK命令(异步删除),Redis 6.0 之后把 AOF fsync 也放到了后台线程。生产环境一定要开启lazyfree-*系列配置,否则删除大 Key 会直接把 Redis 打挂。」
第 40 题:大量 Key 集中过期怎么办?
一句话结论
大量 Key 集中过期会导致:CPU 飙升、响应延迟、甚至超时。解决方案:过期时间加随机偏移 + 开启惰性删除 + 避免大批量 Key 同时设置过期。
深度解析
问题场景:
1 | |
Redis 定期删除的原理(为什么会 CPU 飙升):
1 | |
解决方案:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 过期时间加随机偏移 | EXPIRE key (3600 + random(0, 300)) |
预防集中过期(最常用) |
| 开启惰性删除 | lazyfree-lazy-expire yes |
异步删除过期 Key,不阻塞主线程 |
| 分批设置过期 | 把 10 万个 Key 分成 10 批,每批间隔 1 分钟设置 | 定时任务生成 Key 的场景 |
| 使用滑动过期 | 每次访问时,延长过期时间(如 EXPIRE key 3600) |
热点数据(如用户 Session) |
| 永不过期 + 后台更新 | Key 不设过期,后台定时任务更新缓存 | 数据变更不频繁的场景 |
面试加分回答
「大量 Key 集中过期的本质是惊群效应(Thundering Herd)。除了上述方案,Redis 6.0 之后引入了
dynamic-hz(动态调整定期删除频率),会根据过期 Key 的数量动态调整hz参数(默认 10,最大 500),缓解集中过期的问题。生产环境建议开启dynamic-hz yes(默认已开启)。」
第 41 题:Redis 内存碎片如何产生?如何解决?
一句话结论
内存碎片 = 内存分配器分配的内存 > 实际使用的内存。产生原因:频繁修改数据、键值对大小不一。解决方案:开启自动内存碎片整理(activedefrag)。
深度解析
内存碎片的产生:
1 | |
查看内存碎片率:
1 | |
解决方案:
1 | |
面试加分回答
「内存碎片是 Redis 生产环境的常见问题。Redis 4.0 之前,解决碎片只能重启;Redis 4.0 引入了主动碎片整理(Active Defragmentation),可以在不阻塞主线程的情况下整理内存碎片。原理是:后台线程扫描内存,把相邻的、可被重新利用的内存碎片合并。生产环境建议开启
activedefrag yes,并合理设置active-defrag-threshold-lower(默认 10,表示碎片率 > 1.1 时开始整理)。」
第 42 题:Redis Module 是什么?有哪些常用的 Module?
一句话结论
Redis Module = Redis 的扩展机制(4.0+),可以用 C/C++/Rust 编写自定义模块,扩展 Redis 的功能。常用 Module:RediSearch(搜索引擎)、RedisJSON(JSON 支持)、RedisGraph(图数据库)、RedisTimeSeries(时序数据)、RedisBloom(布隆过滤器)。
深度解析
为什么需要 Redis Module?
1 | |
常用 Redis Module:
| Module | 功能 | 典型场景 |
|---|---|---|
| RediSearch | 全文搜索引擎(支持中文分词) | 商品搜索、文章搜索 |
| RedisJSON | 原生支持 JSON 文档(可以查询/修改 JSON 字段) | 存储复杂对象(不需要序列化) |
| RedisGraph | 图数据库(支持 Cypher 查询语言) | 社交关系、推荐系统 |
| RedisTimeSeries | 时序数据库(支持降采样、聚合查询) | 监控指标、IoT 数据 |
| RedisBloom | 布隆过滤器(去重、判重) | 缓存穿透防护、数据去重 |
| RedisCell | 分布式限流(基于令牌桶算法) | API 限流 |
| RedisAI | 机器学习模型推理 | 实时推荐、图像识别 |
如何使用 Redis Module?
1 | |
面试加分回答
「Redis Module 让 Redis 从『缓存』变成了『多功能数据库』。在生产环境中,RediSearch 和 RedisJSON 最常用:RediSearch 可以实现毫秒级的全文搜索(替代 Elasticsearch 的简单场景);RedisJSON 可以原生存储 JSON 文档,并支持 JSONPath 查询(如
JSON.GET doc $.users[?(@.age > 18)])。另外,Redis 官方提供了 Redis Stack(集成了常用 Module 的安装包),开箱即用,不需要自己编译 Module。」
第 43 题:如何用 Redis 实现延时队列?
一句话结论
方案 1(推荐):Redis Stream + XPENDING(可靠,支持 ACK);(方案 2:Sorted Set + 时间戳,简单但不支持 ACK);(方案 3:Redisson 的延迟队列,生产级)。
深度解析
方案 1:Redis Stream(推荐)
1 | |
方案 2:Sorted Set(简单但不支持 ACK)
1 | |
方案 3:Redisson 的延迟队列(生产级)★ 推荐
1 | |
Redisson 的延迟队列是生产级的:支持 ACK、支持消费者挂了重新投递、支持集群模式。
面试加分回答
「用 Redis 实现延时队列,如果是生产环境,强烈推荐用 Redisson 的延迟队列(Java)或 redis-py 的 BlPop 配合 Sorted Set(Python),不要自己造轮子。另外,如果延时任务量很大(如每天 1 亿个延时任务),或者需要严格的「至少一次投递」,建议用专业的消息队列(如 RocketMQ 的延时消息、RabbitMQ 的 TTL+死信队列),Redis 的延时队列适合「任务量不大、允许极小概率丢失」的场景。」
第 44 题:Redis 7.2 新特性:向量相似度搜索(Vector Similarity Search)
一句话结论
Redis 7.2 引入了向量相似度搜索(VSS),可以直接在 Redis 中存储向量、搜索相似向量,无需部署专用向量数据库(如 Milvus、Pinecone),适合 RAG(检索增强生成)场景。
深度解析
为什么需要向量搜索?
1 | |
Redis 7.2 的向量搜索:
1 | |
Redis 向量搜索 vs 专业向量数据库:
| 对比项 | Redis 7.2 VSS | Milvus / Pinecone |
|---|---|---|
| 部署复杂度 | 低(复用现有 Redis) | 高(需要单独部署) |
| 性能 | 中等 | 高(专为向量搜索优化) |
| 功能完整度 | 基础(HNSW 索引) | 完整(支持多种索引、过滤) |
| 适用场景 | 小规模向量搜索(< 1 亿向量) | 大规模向量搜索(> 1 亿向量) |
面试加分回答
「Redis 7.2 的向量搜索是一个很新的特性(2023 年发布),让 Redis 可以直接用于 RAG(检索增强生成)场景,不需要单独部署向量数据库。在生产环境中,如果向量数据量不大(< 1000 万),用 Redis 做向量搜索是完全够用的;如果向量数据量很大(> 1 亿),或者需要复杂的过滤条件(如「只搜索最近 7 天的文档」),建议用专业的向量数据库(如 Milvus)。另外,Redis 的向量搜索需要安装 RediSearch Module,社区版 Redis 默认不包含。」
第 45 题:Redis 配置优化和性能调优
一句话结论
Redis 性能调优 = 合理设置 maxmemory + 选择合适的内存淘汰策略 + 开启 lazyfree + 调整 TCP backlog + 使用 Pipeline/UNLINK。
深度解析
关键配置参数:
1 | |
性能调优清单:
| 优化项 | 做法 | 效果 |
|---|---|---|
| 使用 Pipeline | 批量发送命令(减少 RTT) | 性能提升 5~10 倍 |
| 使用 UNLINK 代替 DEL | 异步删除大 Key | 避免主线程阻塞 |
| 避免大 Key | 拆分 > 10KB 的 String,> 5000 个元素的 Set/Hash/ZSet | 避免阻塞 |
| 避免热 Key | 本地缓存 + Key 副本 | 分散压力 |
| 合理设置过期时间 | 加随机偏移,避免集中过期 | 避免 CPU 飙升 |
| 使用 Hash/Set 的 ziplist/intset 编码 | 控制 hash-max-ziplist-entries(默认 512) | 节省内存 |
| 使用连接池 | 避免频繁创建/销毁连接 | 减少延迟 |
面试加分回答
「Redis 性能调优是生产环境的核心技能。除了上述配置优化,还有一个很重要的点:监控 Redis 的关键指标(如 QPS、内存使用率、键空间命中率、慢查询数量)。推荐用 Prometheus + Grafana 监控 Redis,或者直接用云服务商(阿里云/腾讯云)的 Redis 监控。另外,Redis 7.0 引入了 list-max-listpack-size 等配置,进一步优化了内存使用,升级到 Redis 7.0+ 也是一个很好的性能优化手段。」
第 46 题:Redis 监控和运维实践
一句话结论
Redis 监控 = INFO 命令(内置) + slowlog(慢查询) + redis-cli –bigkeys/–hotkeys(大 Key/热 Key) + 第三方监控(Prometheus + Grafana)。
深度解析
内置监控命令:
1 | |
第三方监控方案:
1 | |
Redis 运维 checklist:
1 | |
面试加分回答
「Redis 监控是生产环境必不可少的。除了上述方案,Redis 6.0 引入了 ACL LOG 命令(查看 ACL 违规日志),Redis 7.0 引入了 LATENCY HISTORY 命令(查看延迟事件历史),这些都是排查 Redis 性能问题的利器。另外,推荐读一下 Redis 官方的 Redis Latency Problems Troubleshooting 文档,里面详细讲解了如何排查 Redis 延迟问题(如网络延迟、命令执行延迟、持久化延迟等)。」
第 47 题:Redis 6.0 ACL(访问控制列表)详解
一句话结论
Redis 6.0 引入了 ACL(Access Control List),可以创建多个用户,为每个用户设置不同的密码、命令权限、Key 访问权限,替代原来的单用户 + requirepass 模式。
深度解析
为什么需要 ACL?
1 | |
ACL 基本命令:
1 | |
生产环境 ACL 配置建议:
1 | |
面试加分回答
「Redis 6.0 的 ACL 是生产环境安全加固的重要手段。除了 ACL,还可以用 rename-command 重命名/禁用危险命令(如 FLUSHALL、KEYS、SHUTDOWN),双重保险。另外,Redis 的 ACL 支持 外部认证(如 LDAP、OAuth2),适合企业级场景(如用公司的统一认证登录 Redis)。配置方式:在 redis.conf 中设置
aclfile /path/to/users.acl,把 ACL 规则保存到文件(而不是写在 redis.conf 中),方便管理。」
第 48 题:Redis 7.0 新特性详解
一句话结论
Redis 7.0 的重要新特性:Function(替代 Lua 脚本)、ACL 增强、Listpack(替代 Ziplist)、改进的 Vector Set(向量集合)。
深度解析
新特性 ①:Function(函数)
1 | |
1 | |
新特性 ②:Listpack(替代 Ziplist)
1 | |
新特性 ③:改进的 ACL
1 | |
面试加分回答
「Redis 7.0 的 Listpack 是一个很重要的优化,解决了 Ziplist 的连锁更新问题。在生产环境中,如果用到 Hash/List/ZSet,且元素数 < 512,Redis 会自动用 Listpack 编码(替代 Ziplist),内存更省、性能更好。另外,Redis 7.2(最新版本)引入了 Vector Similarity Search(向量相似度搜索),让 Redis 可以直接用于 AI 应用(如 RAG),不需要单独部署向量数据库。」
第 49 题:Redis 主从复制的优化和调优
一句话结论
主从复制优化 = 合理配置 replication backlog size + 开启无磁盘复制(diskless replication) + 监控复制延迟 + 避免主节点写入过大。
深度解析
关键配置参数:
1 | |
监控主从复制延迟:
1 | |
主从复制延迟的优化:
1 | |
面试加分回答
「主从复制延迟是 Redis 生产环境的常见问题。如果业务需要『写完立即读到』(强一致性),可以用 WAIT 命令(阻塞等待从节点确认),但会牺牲写性能。另外,Redis 的复制是异步的,所以主从节点之间的数据一定是有延迟的,这是 CAP 定理决定的(Redis 选择 AP,而不是 CP)。如果一定要强一致性,应该用 Redis Raft(基于 Raft 协议的强一致 Redis),或者用 ZooKeeper/etcd 做分布式锁。」
第 50 题:Redis Cluster 的运维和故障处理
一句话结论
Redis Cluster 运维 = 合理分配 Slot + 监控节点状态 + 处理 Slot 迁移 + 处理节点故障 + 定期备份。
深度解析
Slot 分配原则:
1 | |
Slot 迁移(扩容/缩容):
1 | |
故障处理:
1 | |
Redis Cluster 的注意事项:
1 | |
面试加分回答
「Redis Cluster 的 Slot 迁移是一个『高危操作』,迁移期间会导致部分 Key 访问失败(ASK 重定向)。在生产环境中,迁移 Slot 一定要在业务低峰期进行,并且提前通知业务方。另外,Redis Cluster 的客户端需要支持智能路由(如 JedisCluster、redis-py-cluster),否则每次都要重定向,性能很差。如果觉得 Redis Cluster 太复杂,可以考虑用 Codis(代理模式,客户端不需要支持 Cluster 协议),或者用云服务商提供的 Redis 集群服务(如阿里云的 ApsaraDB for Redis)。」
“””
补充篇:BAT 高频遗漏题(31-40题)
第 31 题:Redis 缓存读写策略有哪几种?
一句话总结:三种策略——Cache Aside(最常用)、Read/Write Through、Write Behind;互联网项目默认用 Cache Aside。
深度解析:
1 | |
面试加分回答:
“我们项目用 Cache Aside 策略。写操作时选择’删除缓存’而不是’更新缓存’,原因是:① 如果写操作频繁,更新缓存开销大 ② 并发写场景下,更新缓存可能有顺序问题(线程A先更新DB,线程B后更新DB,但缓存先写了B再写了A,导致不一致)。删除缓存让下次读时自动加载最新值,更简单可靠。”
第 32 题:Redis 的 String 和 Hash 存储对象哪个更好?
一句话总结:单个对象用 Hash 更省内存(Redis 会编码为 ziplist),需要整体操作或用 TTL 控制过期时用 String。
深度解析:
1 | |
内存对比(重要!):
1 | |
面试加分回答:
“我们存储用户信息时,如果用户对象字段不多(<10个),用 Hash 存储(利用 ziplist 编码省内存);如果用户对象需要设置不同的 TTL,或者需要整体 GET/SET,用 String 存储 JSON。另外,用 Hash 存储时要注意配置 hash-max-ziplist-entries(默认 512),超过这个阈值会转为 hashtable 编码,内存优势就没了。”
第 33 题:Redis 的跳表(Skip List)为什么用跳表而不用红黑树?
一句话总结:跳表和红黑树时间复杂度相同(O(log n)),但跳表实现更简单、范围查询更高效、支持并发修改更容易。
深度解析:
| 对比项 | 跳表(Skip List) | 红黑树(Red-Black Tree) |
|---|---|---|
| 实现难度 | 简单(类似多层链表) | 复杂(旋转操作很难写对) |
| 范围查询 | 高效(找到起点后顺序遍历) | 需要中序遍历,较复杂 |
| 并发修改 | 相对容易(可以无锁化) | 很难(旋转操作需要全局锁) |
| 内存占用 | 稍高(多层指针) | 稍低 |
跳表的原理(用生活比喻):
1 | |
面试加分回答:
“Redis 的有序集合(Sorted Set)底层用跳表而不用红黑树,核心原因是:① 跳表的 range 查询(ZRANGE、ZRANGEBYSCORE)效率极高,找到起点后顺序遍历即可;红黑树做范围查询需要中序遍历,实现复杂。② 跳表的代码实现比红黑树简单太多,不容易出 Bug。③ 跳表支持无锁并发修改(虽然 Redis 单线程不需要),扩展性更好。”
第 34 题:Redis 的 Bitmap 和 HyperLogLog 实战场景?
一句话总结:Bitmap 适合「是/否」类统计(如用户签到、活跃状态),HyperLogLog 适合「UV 去重估算」(误差约 0.81%,但只需 12 KB 内存)。
深度解析:
Bitmap 实战:用户签到
1 | |
HyperLogLog 实战:页面 UV 统计
1 | |
面试加分回答:
“我们用 HyperLogLog 做日活/月活统计,12 KB 内存可以统计 2^64 个不同值,误差约 0.81%,对于 UV 统计完全够用。如果用 Set 存储用户 ID 做去重,100 万 UV 需要约 80 MB 内存,HyperLogLog 只要 12 KB,差距 6000 倍!但要注意 HyperLogLog 只能估算去重数,不能获取’哪些用户来访过’(这个信息已经丢失了)。”
第 35 题:Redis 的大 Key 问题怎么解决?
一句话总结:大 Key(String > 10 KB,Hash/Set/ZSet > 5000 个元素)会导致阻塞、内存不均、迁移慢;解决方案:拆分 Key、定期清理、使用 iScan 渐进删除。
深度解析:
大 Key 的危害:
1 | |
解决方案:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 拆分 Key | 把 user:1001:friends(1 万个好友)拆成 user:1001:friends:0、user:1001:friends:1(每批 1000 个) |
预防大 Key |
| 定期清理 | 用 iScan 渐进式扫描,发现大 Key 后拆分或清理 |
治理已有大 Key |
| 渐进删除 | 不用 DEL,用 UNINK(异步删除)或分批 HDEL |
删除大 Key |
| 监控告警 | 用 redis-cli --bigkeys 定期扫描,超过阈值告警 |
预防 |
面试加分回答:
“我们生产环境用
redis-cli --bigkeys每周扫描一次,发现大 Key 后:① 如果是 List/Set/Hash,拆分成多个小 Key(按 hash(key) % N 分片)② 删除大 Key 不用 DEL,用 UNLINK(Redis 4.0+,异步删除,不阻塞主线程)③ 如果是需要保留的大 Key,用 SCAN + HSCAN/ZSCAN 分批操作,避免一次性操作整个大 Key。”
第 36 题:Redis 的热 Key 问题怎么解决?
一句话总结:热 Key(如秒杀商品、顶流网红)会导致单节点 CPU 打满;解决方案:本地缓存(一级缓存) + Key 副本(分散到多个节点)。
深度解析:
热 Key 的危害:
1 | |
解决方案 1:本地缓存(一级缓存)
1 | |
解决方案 2:Key 副本(分散到多个节点)
1 | |
面试加分回答:
“我们解决热 Key 的标准方案是:① 本地缓存(Caffeine)+ Redis 二级缓存,本地缓存设很短的 TTL(如 5~10 秒),既能大幅降低 Redis 压力,又能保证数据不会太旧 ② 如果本地缓存不够(如单机 QPS 极高),用 Key 副本方案,把热 Key 复制 N 份分散到不同分片。另外,Redis 6.0 的客户端缓存(Client-side caching)也是一个很好的方案,可以让客户端自动追踪 Key 的变化。”
第 37 题:Redis 6.0 的多线程是什么?和单线程冲突吗?
一句话总结:Redis 6.0 的多线程只用于网络 I/O(读/写 Socket),命令执行仍然是单线程的,所以不存在并发安全问题。
深度解析:
1 | |
面试加分回答:
“Redis 6.0 的多线程是一个很大的性能提升(官方测试:单线程 10 万 QPS,多线程可以到 20~30 万 QPS),但很多人误以为 Redis 变成多线程了、会有并发问题——这是错的。命令执行仍然是单线程的,所以 TRANSACTION/MULTI/WATCH 的语义完全不变,不需要担心并发问题。开启方式:
io-threads 4(建议设为 CPU 核数的 1/2 到 1 倍)。”
第 38 题:Redis 的后台线程(Bio 线程)是做什么的?
一句话总结:Redis 把慢操作(如关闭文件描述符、AOF 刷盘、大 Key 异步删除)放到后台线程执行,避免阻塞主线程。
深度解析:
为什么需要后台线程?
1 | |
Redis 的后台线程有哪些?
1 | |
面试加分回答:
“Redis 的 UNLINK 命令(异步删除)就是交给后台线程执行的。我们用 UNLINK 代替 DEL 来删除大 Key,避免了主线程阻塞。另外,AOF 的 fsync 策略如果设为
always(每条命令都刷盘),性能会很差;设为everysec(后台线程每秒 fsync 一次)是性能和可靠性的最佳平衡点,这也是默认配置。”
第 39 题:Redis 和数据库的数据一致性问题怎么解决?
一句话总结:无法做到强一致,只能做到最终一致性;核心是先更新 DB,再删除缓存,并配合延迟双删或消息队列重试。
深度解析:
先更新 DB 还是先删除缓存?
1 | |
延迟双删(进一步降低不一致概率):
1 | |
面试加分回答:
“我们保证最终一致性的方案是:① 先更新 DB,再删除缓存 ② 删除缓存失败时,把删除操作发送到消息队列,异步重试(最多重试 3 次,仍然失败就告警人工介入)③ 缓存设合理的 TTL,即使出现不一致,最多不一致 TTL 秒。另外,如果是要求强一致性的场景(如金融),不应该用缓存,直接读 DB。”
第 40 题:Redis 的部署架构(单机、主从、哨兵、集群)怎么选型?
一句话总结:单机(测试)、主从(读写分离,不保证高可用)、哨兵(高可用,但性能有上限)、集群(水平扩展,生产环境首选)。
深度解析:
| 架构 | 说明 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 单机 | 只有 1 个 Redis 实例 | 简单 | 单点故障,容量有限 | 开发/测试环境 |
| 主从复制 | 1 主 N 从,从库只读 | 读写分离,提升读性能 | 主库挂了需要手动切换 | 读多写少,能接受手动切换 |
| 哨兵(Sentinel) | 主从 + 哨兵集群(监控+自动切换) | 自动故障转移,高可用 | 写操作仍然只有 1 个主库(写性能上限) | 高性能读,中等写 |
| 集群(Cluster) | 数据分片,多个主库 | 水平扩展(写/读都能扩展) | 不支持多 Key 事务、不支持 SELECT | 大数据量、高并发 生产环境首选 |
面试加分回答:
“我们生产环境用的是 Redis Cluster(3 主 3 从,共 6 个节点)。选择集群模式的原因:① 数据量超过单机内存(我们约 20 GB,单机 16 GB 内存不够)② QPS 超过单机上限(我们峰值 15 万 QPS,单机约 10 万 QPS 上限)③ 高可用要求:集群模式自动故障转移,不需要人工介入。另外,使用集群时要注意:mget/mset 如果 Key 不在同一个 slot,会报错,需要用 hash tag(
{user1001}:profile、{user1001}:orders)强制放到同一个 slot。”
Redis 40 题完结!
如果觉得这篇文章对你有帮助,欢迎分享给更多的小伙伴!