Redis 7.0 集群面试题
生成时间:2026-06-06 21:22 侧重:Redis 7.0 集群运维与架构(非 Spring 集成方向)
题目 1:Redis 7.0 有哪些值得关注的新特性?
难度:⭐⭐
标准答案:
Redis 7.0 的核心新特性包括:
- Function API(替代 Lua 脚本):引入
FUNCTION LOAD/FCALL/FCALL_RO,支持将 Lua 脚本以"函数"形式持久化存储到 Redis 中,解决了 Lua 脚本在集群重启后丢失的问题,并支持版本管理。 - 多线程 IO 增强:Redis 7.0 对多线程 IO(Threaded I/O)进行了优化,支持在
io-threads配置下并行处理网络读写,显著提升高并发场景下的吞吐量(官方测试最高提升 2x)。 - ACL 增强:支持更细粒度的权限控制,可以对键、命令、Channel 设置独立的 ACL 规则,并支持
ACL LOG查看权限拒绝记录。 - Cluster 管理命令增强:新增
CLUSTER LINKS、CLUSTER SHARDS等命令,方便运维查看集群拓扑和节点间连接状态。 - List 数据类型新实现(Listpack):List 底层从 quicklist 迁移到 listpack + quicklist 混合结构,减少内存碎片,提升缓存友好性。
- RDB 格式优化:RDB 版本升级到 v10,支持更大字典的增量保存,加速大规模实例的 RDB 生成和加载。
- Client Eviction:支持在内存达到上限时,驱逐客户端输出缓冲区(而非仅返回 OOM 错误),提升稳定性。
- ZSET 的
ZMPOP/BZMPOP:支持原子性地弹出多个有序集合中的成员,适合消息队列场景。
延伸思考:
- Function 与 Lua 脚本的核心区别是什么?为什么推荐在新项目中使用 Function?
- 多线程 IO 是否意味着 Redis 变成了多线程处理命令?为什么?
题目 2:Redis Cluster 的 Hash Slot 机制是如何工作的?
难度:⭐⭐
标准答案:
Redis Cluster 采用 16384 个哈希槽(Hash Slot) 进行数据分片:
- Slot 分配:每个 Key 通过 CRC16 算法计算校验和,再对 16384 取模,得到该 Key 所属的 Slot 编号(0 ~ 16383)。
- Slot 映射到节点:16384 个 Slot 被分配到集群中的不同节点,每个节点负责一部分 Slot。例如,3 个节点的集群,每个节点约负责 5461 个 Slot。
- 客户端路由:客户端计算 Key 的 Slot 后,直接向负责该 Slot 的节点发送请求;如果请求发到了错误的节点,该节点会返回
MOVED重定向响应,客户端据此更新路由缓存。 - ASK 与 MOVED 的区别:
MOVED表示 Slot 已经永久迁移到新节点;ASK表示 Slot 正在迁移过程中,下次请求仍需先尝试原节点。
关键命令:
CLUSTER ADDSLOTS <slot> [slot ...]:为当前节点分配 SlotCLUSTER NODES:查看集群拓扑和 Slot 分布CLUSTER KEYSLOT <key>:查询指定 Key 属于哪个 Slot
延伸思考:
- 为什么 Slot 数量是 16384 而不是更多或更少?(提示:心跳包大小、网络开销、迁移粒度)
- 在集群模式下,多 Key 操作(如
MSET、MGET)有什么限制?如何绕过?
题目 3:Redis Cluster 的故障转移(Failover)机制是怎样的?
难度:⭐⭐⭐
标准答案:
Redis Cluster 的故障转移分为 自动故障转移 和 手动故障转移 两种:
自动故障转移流程
故障检测(PFAIL → FAIL):
- 每个节点定期向其他节点发送 PING;如果在
cluster-node-timeout(默认 15s)内未收到 PONG,则将该节点标记为 PFAIL(可能失效)。 - 当集群中超过半数的主节点都认为某主节点为 PFAIL 时,该节点被标记为 FAIL(确认失效),并广播 FAIL 消息。
- 每个节点定期向其他节点发送 PING;如果在
从节点选举:
- 故障主节点的所有从节点中,会有一个发起选举(基于 Raft 算法的变体)。
- 选举条件:从节点的复制偏移量(replication offset)越大,获得选票的概率越高(数据越新越优先)。
- 获得超过半数主节点投票的从节点赢得选举。
晋升与新拓扑广播:
- 当选从节点执行
SLAVEOF NO ONE,晋升为主节点。 - 新主节点接管原主节点的所有 Slot,并广播
PONG通知集群拓扑变更。
- 当选从节点执行
手动故障转移
通过 CLUSTER FAILOVER [FORCE|TAKEOVER] 命令触发:
- 不带参数:优雅转移,确保数据完全同步后再切换。
FORCE:在从节点未完全同步时也强制执行。TAKEOVER:不经过选举,直接从节点立即晋升(适用于原主节点已无法通信的场景)。
延伸思考:
- 默认
cluster-node-timeout为 15s,这个参数应该如何根据实际场景调整? - 如果集群中有一半或以上的主节点同时故障,会发生什么?
题目 4:Redis 的持久化机制(RDB 与 AOF)有什么区别?如何选型?
难度:⭐⭐
标准答案:
| 维度 | RDB(Redis Database Backup) | AOF(Append Only File) |
|---|---|---|
| 原理 | 定期将内存快照写入磁盘(fork 子进程) | 将每条写命令追加到文件(类似 binlog) |
| 持久化时机 | 可配置(如 save 900 1)或手动 SAVE/BGSAVE | 每个写命令立即或定期 fsync |
| 文件大小 | 紧凑的二进制文件,体积小 | 文本文件,体积较大(可通过 BGREWRITEAOF 重写压缩) |
| 恢复速度 | 快(直接加载内存镜像) | 慢(需要重放所有命令) |
| 数据安全性 | 可能丢失最近一次快照后的所有数据 | 可配置为 always(不丢失)、everysec(丢失 1s)、no(依赖 OS) |
| 性能影响 | fork 时有短暂停顿(COW 机制),之后无影响 | always 模式性能影响大;everysec 较平衡 |
Redis 7.0 的混合持久化
Redis 4.0+ 支持 RDB + AOF 混合持久化(默认开启,aof-use-rdb-preamble yes):
- AOF 重写时,先写入 RDB 格式的全量数据,再追加增量 AOF 命令。
- 重启时先加载 RDB 部分(快),再重放 AOF 增量(少),兼顾速度和安全性。
选型建议
- 允许少量数据丢失、追求快速重启 → RDB
- 要求高数据安全、可以接受稍慢恢复 → AOF(
everysec) - 生产环境推荐 → 混合持久化(RDB + AOF)
延伸思考:
- RDB 的
fork()操作在内存很大时会有什么问题?如何缓解? - AOF 的
everysec模式下,如果磁盘 IO 饱和,Redis 主线程会阻塞吗?
题目 5:Redis 的内存淘汰策略(Eviction Policy)有哪些?应该如何选择?
难度:⭐⭐
标准答案:
当 Redis 内存达到 maxmemory 上限时,根据配置的淘汰策略决定如何处理新写入:
8 种淘汰策略(maxmemory-policy)
- noeviction(默认):不淘汰,对新写入返回 OOM 错误。
- allkeys-lru:从所有 Key 中淘汰最近最少使用(LRU) 的 Key。
- volatile-lru:从设置了过期时间的 Key 中淘汰 LRU 的 Key。
- allkeys-random:从所有 Key 中随机淘汰。
- volatile-random:从设置了过期时间的 Key 中随机淘汰。
- volatile-ttl:从设置了过期时间的 Key 中,淘汰 TTL 最短(即将过期)的 Key。
- allkeys-lfu(Redis 4.0+):从所有 Key 中淘汰最不经常使用(LFU) 的 Key。
- volatile-lfu(Redis 4.0+):从设置了过期时间的 Key 中淘汰 LFU 的 Key。
LRU vs LFU
- LRU(Least Recently Used):关注"最近是否被访问",适合突发流量场景。
- LFU(Least Frequently Used):关注"访问频率",适合长期热点场景(如缓存用户资料)。
选型建议
| 场景 | 推荐策略 |
|---|---|
| 通用缓存 | allkeys-lru |
| 有冷热数据区分,且设置了过期时间 | volatile-lru 或 volatile-lfu |
| 需要保留没有设置过期时间的"永久 Key" | volatile-* 系列 |
| 访问频率差异大(少数 Key 被频繁访问) | allkeys-lfu |
延伸思考:
- Redis 的 LRU 是精确的吗?为什么采用近似 LRU?(提示:性能与内存权衡)
- 如何监控和分析当前实例的 Key 淘汰情况?
题目 6:Redis 7.0 的 Function(函数)是什么?与 Lua 脚本有什么区别?
难度:⭐⭐⭐
标准答案:
Function 是什么?
Redis Function 是 Redis 7.0 引入的 服务器端函数 机制,允许将 Lua 脚本以"函数"的形式持久化存储在 Redis 实例中,并通过 FCALL / FCALL_RO 调用。
与 Lua 脚本(EVAL)的核心区别
| 维度 | Lua 脚本(EVAL / EVALSHA) | Function(FCALL) |
|---|---|---|
| 存储方式 | 每次调用需传递脚本或 SHA1 摘要;重启后丢失 | 持久化存储在 Redis 中(RDB/AOF 均会保存) |
| 生命周期 | 临时(仅在当前实例生命周期内有效) | 永久(除非显式删除 FUNCTION DELETE) |
| 版本管理 | 无 | 支持 FUNCTION LOAD 时指定 REPLACE 更新函数 |
| 调用方式 | EVAL <script> <numkeys> ... 或 EVALSHA <sha1> ... | FCALL <function_name> <numkeys> ... |
| 只读函数 | 无法声明只读,可能路由到主节点 | 可通过 FCALL_RO 声明只读,可路由到从节点 |
| 集群模式 | 需保证所有相关 Key 在同一 Slot(否则报错) | 同 Lua 脚本(需使用 Hash Tag 保证 Key 在同一 Slot) |
使用示例
# 加载函数
FUNCTION LOAD "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return #keys end)"
# 调用函数
FCALL myfunc 2 key1 key2 arg1 arg2延伸思考:
- 在 Redis 7.0 中,为什么推荐使用 Function 而不是 EVAL?
- Function 在集群模式下的限制与 Lua 脚本是否完全相同?
题目 7:Redis 7.0 的多线程 IO 是如何工作的?是否意味着 Redis 变成了多线程?
难度:⭐⭐⭐
标准答案:
多线程 IO 的工作机制
Redis 的多线程 IO(Threaded I/O)是 网络 IO 的多线程化,而非命令执行的并行化:
主线程 仍然负责:
- 接收客户端连接
- 解析命令
- 执行命令(单线程,保证原子性)
- 将响应结果写回客户端(可卸载到 IO 线程)
IO 线程(配置
io-threads数量)负责:- 读取客户端请求(read)
- 将响应写回客户端(write)
- 这两个操作在多核 CPU 上并行执行,提升吞吐量
启用条件:
- 配置
io-threads-do-reads yes(Redis 6.0+ 默认关闭,需手动开启) - 配置
io-threads <N>(建议设置为 CPU 核心数的 1/2 ~ 2/3,最大 128)
- 配置
Redis 仍然是单线程执行命令的
多线程 IO 仅加速网络读写,命令的执行仍然是单线程的(在主线程中顺序执行),因此:
- 命令的原子性仍然得到保证(无需担心并发修改)
- 慢查询(如
KEYS *、大 Value 操作)仍然会阻塞所有后续命令
性能提升场景
多线程 IO 在高并发、大 Value(网络传输耗时长)场景下效果显著;对于小 Value、低并发场景,提升有限。
延伸思考:
- 为什么 Redis 不将命令执行也改为多线程?(提示:锁竞争、复杂度、原子性保证)
- 如何验证当前实例是否真正受益于多线程 IO?
题目 8:Redis Stream 的消息消费模型是怎样的?与 Kafka 有什么异同?
难度:⭐⭐⭐
标准答案:
Redis Stream 的消费模型
Redis Stream 是 Redis 5.0 引入的持久化、可分区、支持消费者组的消息队列:
- 消息 ID:每条消息有一个唯一 ID(格式:
timestamp-seq,如1650000000000-0),可自动生成或手动指定。 - 消费者组(Consumer Group):
- 多个消费者可以组成一个"消费者组",组内每条消息只被一个消费者处理(竞争消费)。
- 不同消费者组之间互相独立,每条消息会被每个组各消费一次(发布-订阅模式)。
- Pending 机制:
- 消费者读取消息后,消息进入
PEL(Pending Entries List),状态为pending。 - 消费者处理完成后,需调用
XACK确认消息,消息才会从 PEL 中移除。 - 未确认的消息可以通过
XCLAIM转移给其他消费者(故障转移)。
- 消费者读取消息后,消息进入
与 Kafka 的对比
| 维度 | Redis Stream | Kafka |
|---|---|---|
| 存储 | 内存 + 可选 AOF/RDB 持久化 | 磁盘(顺序写,吞吐极高) |
| 分区 | 不支持原生分区(需应用层实现) | 原生支持多分区,每个分区有序 |
| 消费语义 | at-least-once(需 XACK) | at-least-once(可配置 exactly-once) |
| 消息保留 | 可配置 MAXLEN 限制长度 | 按时间/大小保留,支持回溯 |
| 适用场景 | 轻量级消息队列、实时性要求高 | 海量消息、高吞吐、需回溯 |
关键命令
# 创建消费者组
XGROUP CREATE mystream mygroup $ MKSTREAM
# 消费消息(组内竞争)
XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream >
# 确认消息
XACK mystream mygroup <message-id>延伸思考:
- 如何使用 Redis Stream 实现"至少一次"和"精确一次"消费语义?
- 在集群模式下,Stream 的 Key 应该如何设计才能保证同一 Stream 的所有数据在同一节点?
题目 9:如何对 Redis Cluster 进行扩容和缩容?需要注意哪些问题?
难度:⭐⭐⭐
标准答案:
扩容(添加新节点)
添加主节点:
- 启动新 Redis 实例,配置为集群模式。
- 使用
CLUSTER MEET <ip> <port>将新节点加入集群。 - 使用
redis-cli --cluster reshard或CLUSTER SETSLOT手动将部分 Slot 迁移到新节点。
添加从节点:
- 将新节点加入集群后,执行
CLUSTER REPLICATE <master-node-id>,使其成为指定主节点的从节点。
- 将新节点加入集群后,执行
缩容(下线节点)
- 下线从节点:直接停止该节点即可(需确保其主节点有其他从节点,或主节点本身健康)。
- 下线主节点:
- 先将该主节点负责的所有 Slot 迁移到其他主节点(
redis-cli --cluster reshard)。 - 确认 Slot 迁移完成后,使用
CLUSTER FORGET <node-id>从集群中移除该节点。
- 先将该主节点负责的所有 Slot 迁移到其他主节点(
关键注意事项
- 迁移过程中服务可用性:Slot 迁移是在线进行的,但在
MIGRATE命令执行期间,涉及的热 Key 会有短暂阻塞(毫秒级)。 - 避免大 Key 迁移:如果某个 Slot 中包含大 Key(如百万级成员的 Set),迁移会非常慢,甚至导致超时。建议提前拆分大 Key。
- 副本数保证:下线主节点前,需确保其 Slot 已迁移,且集群中每个主节点至少有一个从节点(生产环境建议 1 主 2 从)。
- 客户端路由更新:扩容/缩容后,客户端需要更新 Slot 路由缓存(大多数客户端会自动处理
MOVED重定向)。
官方工具
推荐使用 redis-cli --cluster 系列命令,而非手动执行 CLUSTER 命令:
# 扩容:添加新节点并自动重新分片
redis-cli --cluster add-node <new_ip>:<new_port> <existing_ip>:<existing_port>
# 缩容:下线节点(会自动迁移 Slot)
redis-cli --cluster del-node <ip>:<port> <node-id>延伸思考:
- 在迁移 Slot 的过程中,如果客户端正在访问被迁移的 Key,会发生什么?
- 如何评估集群扩容后性能是否达到预期?
题目 10:如何监控 Redis Cluster 的健康状态?有哪些关键指标?
难度:⭐⭐⭐
标准答案:
健康检查的核心维度
集群拓扑健康:
CLUSTER INFO:查看cluster_state(应为ok)、cluster_slots_assigned(应为 16384)、cluster_slots_fail(应为 0)。CLUSTER NODES:检查每个主节点是否有至少 1 个从节点,且从节点状态为connected。
节点间通信健康:
CLUSTER LINKS(Redis 7.0+):查看节点间的 TCP 连接状态,重点关注state(应为connected)和send_buf_free(发送缓冲区不应持续积压)。- 如果
CLUSTER LINKS显示大量disconnected,可能是网络分区或节点故障。
数据同步健康:
INFO replication:检查主从节点的master_link_status(up)、master_repl_offset与从节点的slave_repl_offset差值(应小于 1000,即延迟 < 1MB)。- 如果从节点长时间无法同步,可能是网络问题或主节点
repl-backlog-size配置过小。
资源使用健康:
INFO memory:关注used_memory(不应超过maxmemory的 80%)、mem_fragmentation_ratio(正常 1.0 ~ 1.5,超过 1.5 可能存在内存碎片)。INFO stats:关注evicted_keys(淘汰 Key 数量,持续增加说明内存不足)、expired_keys(过期 Key 数量,正常)、rejected_connections(被拒绝的连接数,应为 0)。INFO persistence:关注rdb_last_bgsave_status(应为ok)、aof_last_write_status(应为ok)。
关键告警指标
| 指标 | 告警阈值建议 |
|---|---|
cluster_state != ok | 立即告警(集群故障) |
cluster_slots_fail > 0 | 立即告警(有 Slot 无主) |
| 主从复制延迟 > 10MB | 警告(可能网络问题或从节点慢) |
used_memory > maxmemory * 0.8 | 警告(内存即将耗尽) |
mem_fragmentation_ratio > 2.0 | 警告(内存碎片严重,建议重启) |
evicted_keys 持续增长 | 警告(内存不足,需扩容或优化) |
监控工具推荐
- Redis 官方:
redis-cli --cluster check <host>:<port>(快速检查集群健康) - Prometheus + Grafana:使用
redis_exporter采集指标,配置仪表盘和告警 - Redis Insight:官方 GUI 工具,可可视化集群拓扑和性能指标
延伸思考:
- 在大规模集群(如 100+ 节点)中,如何高效地监控每个节点的健康状态?
CLUSTER LINKS命令在 Redis 7.0 中解决了之前哪些运维痛点?
生成工具:OpenClaw + GLM-5.1 | 内容仅供参考,请结合实际生产经验使用。