1. 完整回答
Redis Cluster 是 Redis 原生的分布式集群方案,主要解决单机内存容量、写入吞吐量受限以及服务高可用的问题。
它的核心设计是哈希槽分片 + 主从复制 + 自动故障转移。
首先,Redis Cluster 将整个键空间划分为固定的 16384 个哈希槽,每个主节点负责其中一部分。客户端访问某个 key 时,会通过 CRC16 哈希算法计算槽号,再根据槽位和节点的映射关系,将请求发送给对应的主节点。这样就能把数据和请求分散到多台机器上。
其次,每个主节点可以配置从节点,通过异步复制实现数据备份。集群节点之间利用 Gossip 协议交换状态信息。当某个主节点发生故障,并且经过多数主节点确认后,对应的从节点可以通过投票晋升为新主节点,接管原来的哈希槽。
另外,Redis Cluster 支持在线扩缩容。增加节点时,只需要迁移一部分哈希槽及其数据,不需要重新分布整个集群的数据。
它比较适合数据量较大、写入并发较高,同时要求故障自动恢复的缓存场景。
不过,Redis Cluster 使用异步复制,无法保证强一致性,故障切换时可能丢失少量已确认的写入。同时,多键操作通常要求所有 key 位于同一个哈希槽,因此需要合理设计 key。
2. 核心原理与关键概念
2.1 核心原理
(1)数据分片
Redis Cluster 在 key 和 Redis 节点之间引入哈希槽,实现数据分布。
流程:
业务 key → CRC16 哈希计算 → 对 16384 取模 → 哈希槽 → 对应主节点
计算公式:
slot = CRC16(key) % 16384
例如,三个主节点可以分别负责:
| 主节点 | 负责的哈希槽 |
|---|---|
| Master A | 0~5460 |
| Master B | 5461~10922 |
| Master C | 10923~16383 |
哈希槽与主节点的映射关系可以改变,但 key 对应的槽号通常保持不变,因此扩缩容时可以通过迁移槽位及其数据来调整分布。
(2)客户端请求路由
流程:
连接集群 → 获取槽位映射 → 计算 key 的槽号 → 定位目标节点 → 执行命令
-
客户端通常缓存槽位与节点的映射关系。
-
如果访问了错误的节点,Redis 会返回重定向信息。
-
客户端根据
MOVED或ASK调整请求目标。 -
正常情况下,客户端直接访问目标节点,无需经过中心代理。
(3)故障检测与主从切换
流程:
心跳检测 → PFAIL 疑似故障 → FAIL 故障确认 → 从节点申请选举 → 获得多数主节点投票 → 晋升为主节点
关键环节:
-
Gossip 协议:节点通过 PING/PONG 等消息传播集群状态和故障信息。
-
PFAIL:单个节点认为目标节点可能发生故障。
-
FAIL:结合多数主节点的有效故障报告,确认目标节点故障。
-
故障转移:符合条件的从节点获得多数主节点投票后,接管故障主节点的槽位。
(4)在线扩缩容
流程:
新增节点 → 加入集群 → 选择待迁移槽位 → 迁移槽位数据 → 更新槽位归属 → 客户端更新路由
传统槽迁移过程中,源节点和目标节点会分别进入 MIGRATING、IMPORTING 状态,客户端通过 ASK 重定向访问正在迁移的数据。
Redis 8.4 还引入了原子槽迁移机制,进一步减少迁移期间的请求影响。
2.2 关键概念
| 概念 | 含义 |
|---|---|
| 哈希槽(Hash Slot) | 数据分片的逻辑单位,总数固定为 16384 |
| 主节点(Master) | 负责一部分哈希槽,处理对应数据的读写请求 |
| 从节点(Replica) | 异步复制主节点的数据,支持故障接管和可选的只读访问 |
| Gossip 协议 | 节点间交换拓扑、心跳和故障状态信息的机制 |
| MOVED | 告知客户端某个槽当前归其他节点负责,应更新路由 |
| ASK | 槽迁移期间的临时重定向,通常只针对当前请求 |
| Hash Tag | 利用 key 中的 {} 指定哈希计算部分,使相关 key 进入同一槽 |
3. 高频面试追问
3.1 Redis Cluster 和主从复制、哨兵模式有什么区别?
核心区别是 Redis Cluster 同时支持数据分片和自动故障转移,可以扩展整体存储容量和写入能力。
主从复制主要用于数据备份和读写分离。主节点负责写入,从节点复制主节点的数据,但主节点故障后,需要额外机制完成自动切换。
哨兵模式在主从复制基础上增加了故障监控、主节点选举和自动切换能力,实现了高可用。不过,同一个主从复制组仍然保存相同的数据,整体写入能力和可存储的数据量主要受单个主节点限制。
Redis Cluster 进一步引入哈希槽,把数据分散到多个主节点。各主节点处理不同的数据分片,同时通过从节点提供故障恢复能力。
所以,如果单个主节点已经能够满足容量和性能要求,哨兵模式通常更简单;如果需要水平扩展存储容量和写入吞吐量,Redis Cluster 更合适。
3.2 Redis Cluster 为什么使用 16384 个哈希槽?
哈希槽主要是为了解耦数据和物理节点,16384 则是槽位数量与集群通信开销之间的工程权衡。
如果直接使用 hash(key) % 节点数 分配数据,节点数量变化时,大量 key 的映射结果都会改变,数据迁移成本很高。
Redis Cluster 引入固定数量的哈希槽,让 key 先映射到槽,再由槽映射到具体节点。扩缩容时,只需要调整部分槽位的归属,并迁移这些槽中的数据。
至于为什么选择 16384,主要考虑两个因素:
第一,槽位数量要足够多,支持多个主节点之间相对灵活的数据分布。
第二,集群节点会通过心跳消息交换槽位配置信息。16384 个槽可以使用 2KB 位图表示;如果使用 65536 个槽,就需要 8KB,会增加节点之间的通信开销。
因此,16384 个槽能够兼顾分片灵活性和集群通信效率。
3.3 Redis Cluster 的 MOVED 和 ASK 有什么区别?
MOVED 表示槽位归属发生变化,ASK 表示槽迁移过程中的临时重定向。
Redis Cluster 的客户端通常会缓存槽位到节点的映射关系。
当客户端访问错误的节点时,可能收到 MOVED。这说明当前请求对应的槽应该由其他节点负责。客户端需要向新节点重新发送请求,并更新本地槽位映射,或者刷新完整的集群拓扑。
ASK 主要发生在传统槽迁移过程中。因为一个槽内的 key 可能正在逐步迁移,一部分数据还在源节点,另一部分已经到了目标节点。
收到 ASK 后,客户端会先向目标节点发送 ASKING,再执行当前命令,但暂时不会修改槽位的长期归属关系。
简单来说:
-
MOVED:重新定位槽位,更新路由。
-
ASK:临时访问目标节点,暂不更新路由。
继续追问:Redis Cluster 客户端为什么需要支持集群模式?
因为客户端需要完成槽位计算、请求路由、拓扑更新和重定向处理。普通单节点客户端如果无法处理这些逻辑,扩缩容或故障切换后就可能持续访问错误节点。因此,实际项目通常使用支持 Redis Cluster 的客户端,并确保客户端能够访问集群节点公布的地址。
3.4 Redis Cluster 的主节点故障后,如何自动完成故障转移?
Redis Cluster 通过故障检测、多数主节点确认和从节点选举完成自动故障转移。
整个过程可以分为四步。
第一步,节点之间通过 Gossip 协议交换心跳消息。如果某个节点在规定时间内无法联系到目标节点,就会将其标记为 PFAIL,表示疑似故障。
第二步,节点收集其他主节点的故障报告。如果在有效时间内获得多数主节点的故障判断,就可以将目标节点标记为 FAIL,确认故障。
第三步,故障主节点对应的从节点发起选举。候选从节点需要满足复制数据新鲜度等条件,再向集群中的主节点请求投票。获得多数主节点的投票后,才能晋升为新主节点。
第四步,新主节点接管原来的哈希槽,并将新的槽位归属信息传播给集群。客户端随后更新路由,继续访问新主节点。
如果原来的主节点恢复连接,通常会根据更新后的集群配置转为新主节点的从节点。
这个机制能够避免单个节点误判就直接触发故障转移,同时通过多数派投票降低出现冲突主节点的风险。
3.5 为什么 Redis Cluster 通常部署 3 主 3 从?主节点故障后一定可用吗?
3 主 3 从是 Redis Cluster 常见的高可用部署方案,但并不能保证任意故障情况下都可用。
Redis Cluster 需要多数主节点参与故障确认和选举。因此,至少三个主节点才能比较合理地支持多数派故障转移。
三个主节点负责不同的哈希槽,每个主节点配置一个从节点,就形成了 3 主 3 从的结构。
假设 A、B、C 是三个主节点,A1、B1、C1 分别是对应从节点。
如果 A 故障,B 和 C 仍然构成多数派,A1 在符合选举条件时可以晋升为主节点,接管 A 原来的槽位。
但如果 A 和 A1 同时故障,就没有可用副本接管 A 的数据分片。在默认要求完整槽位覆盖的配置下,集群会进入不可用状态。
另外,即使从节点正常,如果无法获得多数主节点的投票,也无法完成常规自动故障转移。
因此,实际部署中还需要合理安排主从节点的物理位置,尽量避免同一个主从复制组同时受到机器或可用区故障影响。
3.6 Redis Cluster 能保证数据不丢失吗?为什么写入成功还可能丢失?
Redis Cluster 不能保证数据绝对不丢失,因为默认采用异步主从复制,不提供强一致性保证。
正常情况下,客户端向主节点写入数据,主节点执行成功后就可以返回响应,数据再异步同步到从节点。
假设客户端写入一条数据后收到了成功响应,但主节点还没来得及将这条数据复制给从节点就发生故障。
此时,从节点即使成功晋升为新主节点,也可能没有这条数据,造成已确认写入丢失。
网络分区也存在类似风险。例如,旧主节点在少数派网络中短暂接受写入,而多数派网络已经完成新主节点选举。网络恢复后,旧主节点会跟随新的主节点,少数派期间的部分写入可能丢失。
可以通过 WAIT 等命令等待一定数量的从节点确认复制,降低数据丢失概率。但即使收到复制确认,也无法保证故障切换时一定选择包含该写入的副本。
所以,Redis Cluster 适合能够容忍一定数据丢失风险的缓存场景。对于订单、资金等关键业务数据,通常需要使用具备相应可靠性保证的持久化存储作为最终数据依据。
继续追问:Redis Cluster 可以直接读取从节点吗?
可以,但默认情况下读请求主要由对应主节点处理。客户端可以通过 READONLY 模式读取从节点的数据,以分担主节点的读取压力。不过主从复制存在延迟,从节点可能返回旧数据,因此适合能够接受短暂数据不一致的读取场景。
3.7 Redis Cluster 如何实现扩容和缩容?迁移期间会影响请求吗?
Redis Cluster 通过迁移哈希槽及其数据实现在线扩缩容,迁移期间可能出现重定向和短暂延迟。
扩容时,首先添加新的主节点,然后从现有主节点中选择一部分槽位迁移到新节点。
在传统迁移机制中,源节点将槽标记为 MIGRATING,目标节点将槽标记为 IMPORTING,随后逐步迁移对应的 key。
迁移期间,如果请求访问的 key 还在源节点,源节点可以继续处理;如果 key 已经迁移到目标节点,源节点可以通过 ASK 引导客户端临时访问目标节点。
等一个槽的数据迁移完成后,集群会正式更新槽位归属关系,后续请求直接路由到新节点。
缩容过程类似,只需要先迁出目标节点负责的槽,再移除节点。
需要注意,传统迁移过程中,跨多个 key 的操作可能短暂返回 TRYAGAIN,客户端需要具备适当的重试能力。
Redis 8.4 引入了原子槽迁移机制,能够进一步降低迁移期间的访问影响。
因此,扩缩容虽然支持在线执行,但仍然需要关注迁移速度、请求延迟、内存占用和业务流量变化。
3.8 Redis Cluster 为什么会出现 CROSSSLOT?如何处理跨槽操作?
CROSSSLOT 表示一次需要共同执行的多键操作涉及不同哈希槽,超出了 Redis Cluster 的支持范围。
Redis Cluster 将不同槽的数据分布到不同主节点上,因此很多多键命令、事务和 Lua 脚本要求相关 key 位于同一个哈希槽。
例如,使用 MGET 同时读取 user:1:name 和 user:2:name,如果两个 key 属于不同槽,就会出现 CROSSSLOT 错误。
常见解决方式是 Hash Tag。
例如:
-
user:{100}:name -
user:{100}:balance
这两个 key 都以 {100} 作为哈希计算部分,因此会进入同一个槽,可以参与同槽多键操作。
如果业务确实需要访问大量不同槽的数据,可以在应用层拆分成多个独立请求,再合并结果,但这种方式无法提供原来单次操作的整体原子性。
使用 Hash Tag 时还要控制数据分布,避免把大量无关 key 集中到同一个槽,导致负载倾斜。
继续追问:两个 key 在同一个主节点上,但属于不同槽,能执行 MGET 吗?
对于原生 Redis Cluster,通常仍然不能。多键命令的路由限制以哈希槽为单位,即使两个槽碰巧分配给同一个主节点,也必须满足同槽要求才能执行这类操作。
3.9 Redis Cluster 能解决热点 key 和大 key 问题吗?
Redis Cluster 能分散整体数据和请求压力,但无法通过简单增加节点自动解决单个热点 key 或大 key 的问题。
因为一个 key 只会映射到一个哈希槽,而一个槽在稳定状态下由一个主节点负责,所以同一个热点 key 的写入压力仍然集中在单个主节点上。
例如,某个活动配置每秒被访问几十万次,即使集群拥有很多主节点,这些请求仍然会集中访问它所在的分片。
对于热点 key,可以根据实际场景考虑本地缓存、客户端缓存、从节点分担读请求,或者调整数据结构和业务模型,实现更细粒度的拆分。
对于大 key,需要关注内存占用、单次操作耗时,以及迁移和删除时的资源消耗。可以通过拆分数据、控制集合大小、使用非阻塞删除等方式降低影响。
另外,Redis Cluster 的槽位分配相对均匀,也不意味着各节点的内存和请求负载一定均匀,因为不同 key 的数据大小和访问频率可能差别很大。
因此,实际运行中还要持续观察各主节点的 CPU、内存、网络流量以及热点槽位情况。
3.10 Redis Cluster 在实际项目中出现访问异常,通常如何排查?
通常先判断是集群故障、客户端路由异常,还是节点负载问题,再针对性处理。
首先,通过 CLUSTER INFO 查看集群整体状态,重点关注 cluster_state、槽位覆盖情况,以及是否存在故障槽位。
其次,通过 CLUSTER NODES 查看主从角色、节点连接状态、故障标记和槽位分布。通过 CLUSTER SHARDS 可以获取更结构化的分片拓扑信息。
如果出现 CLUSTERDOWN,重点检查是否失去多数主节点、是否有槽位无法正常服务,以及主从故障转移是否成功。
如果频繁出现 MOVED,需要检查客户端是否及时刷新槽位映射,以及集群是否正在迁移或切换。偶发重定向通常属于正常现象,持续大量重定向则需要进一步排查。
如果出现访问延迟升高,就需要观察各节点 CPU、内存、网络、慢命令和主从复制延迟,并检查是否存在热点 key、大 key 或数据分布不均。
实际使用时,还会关注客户端连接池、超时和重试配置,避免节点切换期间的临时故障被放大成业务异常。
4. 易混淆概念对比
4.1 主从复制、哨兵模式与 Redis Cluster
| 对比项 | 主从复制 | 哨兵模式 | Redis Cluster |
|---|---|---|---|
| 数据分片 | 不支持 | 不支持 | 支持 |
| 自动故障转移 | 不提供 | 支持 | 支持 |
| 多主节点分担写入 | 不支持 | 不支持 | 支持 |
| 扩展总存储容量 | 受单主节点限制 | 受单主节点限制 | 可以水平扩展 |
| 主要用途 | 数据复制、读扩展 | 单分片高可用 | 分布式存储和高可用 |
4.2 哈希槽与一致性哈希
| 对比项 | 哈希槽 | 一致性哈希 |
|---|---|---|
| 核心思想 | key 映射到固定数量的槽,槽映射到节点 | key 和节点通过哈希环建立映射 |
| 节点变化 | 调整槽位归属并迁移数据 | 调整哈希环上的节点映射 |
| 数据迁移 | 以槽位为管理单位,实际迁移其中的 key | 主要迁移受哈希环变化影响的数据 |
| Redis Cluster | 原生采用 | 原生 Cluster 未采用 |
二者都能降低节点变化带来的数据迁移成本。Redis Cluster 选择哈希槽,便于明确管理每个分片的数据范围。
4.3 PFAIL 与 FAIL
| 对比项 | PFAIL | FAIL |
|---|---|---|
| 含义 | 疑似故障 | 已确认故障 |
| 判断依据 | 节点本地检测 | 多数主节点的有效故障报告 |
| 是否触发自动故障转移 | 单独不足以触发 | 满足其他条件后可以触发 |
| 主要作用 | 初步识别故障 | 集群级故障确认 |
4.4 MOVED 与 ASK
| 对比项 | MOVED | ASK |
|---|---|---|
| 发生场景 | 槽位归属发生变化或路由信息过期 | 传统槽位迁移过程中 |
| 重定向类型 | 持续性路由调整 | 临时重定向 |
| 客户端更新槽位映射 | 需要更新或刷新 | 暂不更新 |
| 是否发送 ASKING | 不需要 | 需要 |
| 后续请求 | 使用更新后的路由 | 通常继续使用原来的槽位路由 |
4.5 主从复制与强一致性
| 对比项 | Redis Cluster 异步复制 | 强一致性复制方案 |
|---|---|---|
| 写入成功条件 | 默认主节点完成写入即可响应 | 取决于协议,通常要求满足规定的提交条件 |
| 复制延迟 | 存在 | 由具体机制决定 |
| 故障时已确认写入 | 可能丢失 | 在一致性和故障模型保证范围内不丢失已提交数据 |
| 主要取舍 | 优先性能和可用性 | 更严格的数据正确性保证 |
WAIT 能提高 Redis 写入的复制安全性,但不会让 Redis Cluster 变成强一致性系统。
5. 总结
Redis Cluster 是 Redis 原生的分布式集群方案,主要通过哈希槽分片、主从复制和自动故障转移解决单机容量、写入吞吐量和高可用问题。它将数据映射到固定的 16384 个哈希槽,由不同主节点分别负责,客户端根据槽位直接路由请求。集群节点通过 Gossip 协议交换状态,主节点故障后由从节点通过多数派选举完成接管,同时支持通过迁移哈希槽实现在线扩缩容。相比哨兵模式,Redis Cluster 可以把数据和写入压力分散到多个主节点。它的主要限制是异步复制无法保证强一致性,跨槽多键操作受到限制,单个热点 key 也无法自动分散到多个主节点。