目录
3.5 槽位迁移 (Slot Migration) - 集群扩容/缩容/再平衡
1、概念
原理: Redis 官方的分布式解决方案。将数据自动分片到多个节点(每个节点是主从结构)。数据按 槽 划分(共 16384 个槽),每个节点负责一部分槽。主要解决单机 Redis 在内存容量、吞吐量和高可用性方面的瓶颈。
核心目标与解决的问题:
-
海量数据存储: 突破单机内存限制,将数据分片(Shard) 存储在多个节点上,总容量是集群中所有节点内存之和。
-
高吞吐量: 通过分片,将读写请求分散到多个节点并行处理,显著提升整体吞吐量。
-
高可用性 (HA): 每个数据分片都是一个主从复制组(Master-Replica Group)。当主节点故障时,集群能自动进行故障转移,提升从节点为主节点,保证服务可用性。
-
去中心化与自治: 集群节点间通过 Gossip 协议交换信息,管理配置和故障检测。不需要像 Sentinel 那样的独立协调组件(虽然内部机制类似)。
-
水平扩展: 可以方便地通过增加节点来扩展集群的容量和吞吐量。
核心概念:数据分片 (Sharding) 与 Hash Slot (哈希槽):
-
Redis Cluster 将整个数据空间划分为 16384 个固定数量的槽 (Slot) (0-16383)。
-
每个键 (Key) 通过 CRC16 算法计算出一个 16 位的哈希值,然后对这个值取模 16384 (
CRC16(key) % 16384),确定其属于哪个 Slot。 -
每个主节点 (Master) 负责处理一组 Slot 的子集。 集群启动或配置变更时,会明确分配哪些 Slot 由哪个主节点负责。
-
优点:
-
解耦数据与节点: 数据分布由 Slot 决定,节点变化时只需移动 Slot 映射关系,不需要重新计算所有 Key 的哈希。
-
可管理性: 固定数量的 Slot 便于管理、迁移和再平衡。
-
高效路由: 客户端或代理可以快速计算出 Key 对应的 Slot,进而找到负责节点。
-
优点: 真正的分布式,支持水平扩展(存储容量和吞吐量)、高可用。
缺点: 架构更复杂;不支持跨槽的事务(Multi-Key 操作限制);客户端需要支持 Cluster 协议;某些管理操作(如迁移大 key)可能影响性能。
2、集群架构与角色
节点角色 (Node Roles):
-
主节点 (Master):
-
负责处理其分配的 Slot 的所有读写请求。
-
存储数据。
-
参与故障检测与投票。
-
-
从节点 (Replica):
-
复制其关联的主节点数据(异步复制)。
-
处理读请求(可配置
replica-read-only)。 -
在主节点故障时,参与故障转移竞选,有可能被提升为新的主节点。
-
-
一个集群至少需要 3 个主节点才能正常工作(满足故障转移投票要求)。 通常建议为每个主节点配置至少 1 个从节点以实现高可用。
集群拓扑:
-
一个 Redis Cluster 由多个节点(通常是 6 个或更多:3 主 + 3 从)组成。
-
每个节点都知道集群中其他所有节点的状态信息(通过 Gossip 协议)。
-
数据分布在所有主节点上(通过 Slot)。
-
从节点提供数据冗余和故障转移能力。
示例 (3 主 3 从):每个主节点负责约 1/3 的 Slot,其从节点复制其数据。
Master1 (Slots 0-5460) ---- Replica1 (of Master1)
Master2 (Slots 5461-10922) ---- Replica2 (of Master2)
Master3 (Slots 10923-16383) ---- Replica3 (of Master3)

3、核心工作机制
3.1 请求路由 (Request Routing)
客户端如何知道一个 Key 请求应该发给哪个节点?
1> 客户端重定向 (MOVED/ASK): 最常用且推荐的方式。 客户端库需要支持 Cluster 协议。
-
首次请求: 客户端随机连接集群中任一节点发送命令。
-
命中 Slot: 如果该 Slot 由当前节点负责,直接执行命令并返回结果。
-
未命中 Slot (MOVED 错误): 如果该 Slot 不属于当前节点负责,节点返回
MOVED <slot> <target-ip>:<target-port>错误。-
客户端收到
MOVED错误后,更新本地 Slot 映射缓存,然后重新连接到正确的<target-node>发送请求。
-
-
ASK 重定向 (迁移中): 当 Slot 正在从一个节点 (
source) 迁移到另一个节点 (target) 时:-
如果 Key 还在
source节点,source节点正常处理。 -
如果 Key 已迁移到
target节点,source节点返回ASK <slot> <target-ip>:<target-port>错误。 -
客户端收到
ASK后,不更新本地缓存,而是临时连接到target节点,并在发送命令前先发送ASKING命令,然后发送原命令。target节点在迁移期间会接受标记了ASKING的请求。
-
-
智能客户端 (Smart Client): 成熟的 Redis 客户端库 (Lettuce, Jedis, Redisson, ioredis) 都实现了 Cluster 支持。它们在内部维护一个 Slot 到节点地址的映射缓存。通过
CLUSTER SLOTS命令或处理MOVED/ASK响应来更新缓存,尽量将请求直接发送到正确的节点,减少重定向次数。
2> 代理模式 (Proxy): 使用独立的代理服务器 (如 Twemproxy, Codis, Redis Cluster Proxy)。客户端连接代理,代理负责计算 Slot 并转发请求到正确的后端节点。对客户端透明,但增加了额外跳转和潜在瓶颈。
3.2 节点间通信 (Gossip Protocol)
集群节点如何感知彼此的状态(在线、下线、角色、Slot 分配)?
-
PING/PONG 消息: 每个节点默认每秒会随机选择几个其他节点发送
PING消息。接收节点回复PONG消息。 -
消息内容:
PING/PONG消息携带发送节点自身的信息以及它所知道的部分其他节点的信息(不是全部,Gossip 的特点)。 -
传播: 节点通过持续交换
PING/PONG消息,以指数级速度将集群状态信息传播到所有节点。最终所有节点会达到对集群状态的一致视图(最终一致性)。 -
信息类型:
-
节点 ID、IP、端口、角色 (主/从)、状态。
-
节点负责的 Slot 信息。
-
主节点的从节点信息。
-
节点下线/故障信息。
-
-
优点: 去中心化、容错性好(部分节点失效不影响整体)、带宽消耗相对可控(每次只发部分信息)。
-
缺点: 状态传播有延迟(最终一致性),不适用于要求强一致性的场景。
3.3 故障检测 (Failure Detection)
如何判断一个节点是否故障?
-
主观下线 (PFAIL - Possible Fail):
-
节点 A 在
cluster-node-timeout(默认 15 秒) 内一直未能与节点 B 成功通信(未收到有效PONG回复)。 -
节点 A 会在其本地将节点 B 标记为 PFAIL。
-
PFAIL 是主观的,只代表节点 A 认为 B 可能下线。
-
-
客观下线 (FAIL):
-
当节点 A 将节点 B 标记为 PFAIL 后,它会通过
PING/PONG消息询问集群中其他节点对节点 B 的判断。 -
如果集群中大多数主节点(
> N/2,N 是主节点总数)在一定时间窗口内都报告节点 B 为 PFAIL 状态。 -
节点 A 就会将节点 B 标记为 FAIL,并将这个 FAIL 状态通过 Gossip 广播给整个集群。
-
一旦节点 B 被标记为 FAIL,集群将触发对该节点负责的 Slot 的故障转移(如果它是主节点)。
-
3.4 故障转移 (Failover)
当负责某些 Slot 的主节点被标记为 FAIL 时,如何恢复服务?
1> 从节点资格检查: 故障主节点的所有从节点会检查自己是否有资格参与选举:
-
与主节点断开连接的时间不能超过
(cluster-node-timeout * cluster-replica-validity-factor) + 1000毫秒(默认15s * 10 + 1000ms = 160s)。如果断开太久,数据可能太旧,失去资格。
2> 从节点延迟检查: 有资格的从节点会等待一段延迟时间 (replica 排名 * 1000 + random),排名由复制偏移量 (repl_offset) 决定,偏移量最大的(数据最新的)从节点排名为 0。数据最新的从节点等待时间最短。
3> 发起选举: 等待时间结束后,从节点向集群中所有主节点发送 FAILOVER_AUTH_REQUEST 消息,请求投票。
4> 主节点投票:
-
每个主节点在一个配置纪元 (epoch) 内对每个主节点故障事件只能投一次票。
-
主节点收到请求后,如果满足以下条件则投票 (
FAILOVER_AUTH_ACK):-
请求的从节点所属的主节点确实被标记为 FAIL。
-
该主节点在当前纪元尚未投票。
-
请求的从节点是有资格的(断开时间未超限)。
-
5> 赢得选举: 从节点如果在 2 * cluster-node-timeout 时间内收到大多数主节点 (> N/2) 的投票,则赢得选举。
6> 提升为主节点: 赢得选举的从节点:
-
更新自身状态为主节点 (
MASTER)。 -
接管原主节点负责的所有 Slot。
-
通过
PONG消息向集群广播自己成为新主节点的信息。
7> 其他从节点切换: 原主节点下的其他从节点会检测到新主节点上线(通过 Gossip),并开始向新主节点发起复制 (REPLICAOF)。
8> 旧主节点恢复: 如果旧主节点重新上线,它会发现自己负责的 Slot 已被新主节点接管,并自动成为新主节点的从节点。
3.5 槽位迁移 (Slot Migration) - 集群扩容/缩容/再平衡
如何将 Slot 从一个节点移动到另一个节点?
1> 发起迁移命令: 使用 CLUSTER SETSLOT <slot> IMPORTING <source-node-id> 在目标节点上设置 Slot 为 IMPORTING 状态。使用 CLUSTER SETSLOT <slot> MIGRATING <target-node-id> 在源节点上设置 Slot 为 MIGRATING 状态。
2> 迁移键值对:
-
使用
CLUSTER GETKEYSINSLOT <slot> <count>在源节点获取属于该 Slot 的 Key 列表。 -
对每个 Key,使用
MIGRATE <target-host> <target-port> "" 0 <timeout> KEYS <key1> [<key2> ...]命令将 Key 的数据原子性地迁移到目标节点。迁移过程中,源节点会阻塞对该 Key 的操作直到迁移完成。
3> 更新 Slot 分配: 当 Slot 中的所有 Key 都迁移完毕后,使用 CLUSTER SETSLOT <slot> NODE <target-node-id> 命令在所有主节点上执行,将该 Slot 的负责权正式分配给目标节点。这个命令会通过 Gossip 传播到整个集群。
4> 客户端处理 (ASK 重定向): 在迁移过程中,客户端请求:
-
如果 Key 仍在源节点,源节点正常处理。
-
如果 Key 已迁移到目标节点,源节点返回
ASK重定向,引导客户端临时访问目标节点。 -
迁移完成后,客户端会收到
MOVED重定向,更新本地缓存指向目标节点。
5> 自动化工具: 实际操作中,强烈建议使用 redis-cli --cluster reshard <host>:<port> 或 redis-cli --cluster rebalance <host>:<port> 等工具来自动化完成 Slot 迁移和集群再平衡,它们内部封装了上述步骤并处理了复杂性。
4、关键特性与限制
1> 最小主节点数: 3 个(满足投票大多数 > N/2 的要求,3/2=1.5,取整为2,所以需要至少2票)。
2> 键操作限制:
-
多键操作限制: 涉及多个 Key 的命令(如
MGET,MSET,DEL key1 key2,SINTER/SUNION, Lua 脚本中的多键操作)要求所有 Key 必须位于同一个 Slot。否则会返回CROSSSLOT错误。解决方法:-
使用
Hash Tags:在 Key 中使用{}包裹一个部分(如user:{123}:profile,user:{123}:orders),集群只根据{}内的字符串计算 Slot,确保相关 Key 落在同一 Slot。 -
客户端拆分命令,分别发送到不同节点,再合并结果。
-
-
事务限制: 事务 (
MULTI/EXEC) 中的所有命令同样要求 Key 在同一个 Slot(因为事务在单个节点执行)。同样依赖Hash Tags。 -
Lua 脚本限制: 脚本中的所有 Key 必须由执行脚本的节点负责(即在同一节点),同样要求使用
Hash Tags或确保脚本只操作单个 Key。Redis 7.0 引入了functions,对 Cluster 更友好。
3> 从节点读: 支持,通过 READONLY 命令或配置 replica-read-only yes。客户端需显式连接到从节点。存在读取到旧数据(复制延迟) 的风险。
4> Pub/Sub: 默认情况下,发布和订阅操作会广播到所有节点。这意味着 PUBLISH 命令会在发布者连接的节点执行,然后广播到集群所有节点,再由这些节点发送给本地连接的订阅者。也可以使用 SSUBSCRIBE 只订阅特定分片 (shardchannel)。
5> 不支持多数据库: 集群模式下只支持 DB 0。SELECT 命令被禁用。
6> 批量操作效率: 涉及多个 Slot 的批量操作效率低于单机 Redis,因为需要与多个节点通信。
7> 配置管理: 集群配置信息自动在节点间同步(通过 Gossip 和 CLUSTER 命令)。节点列表和 Slot 分配信息存储在 nodes.conf 文件中。
5、部署方案
5.1 集群架构规划
最小生产环境要求:
-
6个节点:3个主节点(Master)+ 3个从节点(Replica)
-
16384个哈希槽:平均分配到主节点
-
节点分布:建议跨物理机/可用区部署
示例拓扑(3主3从):
节点1: 192.168.1.101:6379 (主) - 槽范围 0-5460
节点2: 192.168.1.102:6379 (主) - 槽范围 5461-10922
节点3: 192.168.1.103:6379 (主) - 槽范围 10923-16383
节点4: 192.168.1.104:6379 (从) -> 复制节点1
节点5: 192.168.1.105:6379 (从) -> 复制节点2
节点6: 192.168.1.106:6379 (从) -> 复制节点3
5.2 节点配置(所有节点)
通用配置项 (redis.conf):
# 基础配置
port 6379
daemonize yes
pidfile /var/run/redis_6379.pid
logfile "/var/log/redis/redis_6379.log"
dir /data/redis
# 集群配置
cluster-enabled yes # 启用集群模式
cluster-config-file nodes-6379.conf # 集群状态文件
cluster-node-timeout 15000 # 节点超时时间(毫秒)
cluster-replica-validity-factor 10 # 副本有效性因子
cluster-migration-barrier 1 # 主节点最少副本数
# 安全配置(推荐)
requirepass "ClusterPwd123" # 节点访问密码
masterauth "ClusterPwd123" # 主从认证密码
# 性能优化
maxmemory 8gb # 根据内存调整
maxmemory-policy volatile-lru # 内存淘汰策略
appendonly yes # 开启AOF
appendfsync everysec # AOF刷盘策略
关键参数说明:
-
cluster-node-timeout:节点通信超时时间,影响故障检测速度 -
cluster-replica-validity-factor:控制副本是否参与故障转移(0=始终参与) -
requirepass和masterauth:必须相同,用于节点间通信认证
5.3 集群部署流程
1> 准备节点
# 在所有节点创建数据目录
sudo mkdir -p /data/redis
sudo chown redis:redis /data/redis
# 复制配置文件
sudo cp redis.conf /etc/redis/redis_6379.conf
# 启动所有节点
redis-server /etc/redis/redis_6379.conf
2> 创建集群
# 使用redis-cli创建集群
redis-cli --cluster create \
192.168.1.101:6379 \
192.168.1.102:6379 \
192.168.1.103:6379 \
192.168.1.104:6379 \
192.168.1.105:6379 \
192.168.1.106:6379 \
--cluster-replicas 1 \
-a ClusterPwd123
命令参数说明:
-
--cluster-replicas 1:每个主节点分配1个副本 -
-a:集群密码认证
3> 验证集群状态
# 连接任意节点
redis-cli -c -h 192.168.1.101 -p 6379 -a ClusterPwd123
# 检查集群状态
> CLUSTER INFO
cluster_state:ok # 集群状态正常
cluster_slots_assigned:16384 # 所有槽已分配
cluster_slots_ok:16384
cluster_known_nodes:6 # 节点数量正确
# 查看节点拓扑
> CLUSTER NODES
5.4 主从节点配置详解
主节点配置要点
-
无需特殊配置:主节点自动处理分片数据
-
槽分配:创建集群时自动分配,也可手动调整
-
写操作:只处理属于自己槽的写请求
从节点配置要点
-
自动复制:创建集群时通过
--cluster-replicas参数指定 -
故障转移:主节点故障时,副本自动晋升为主节点
-
只读模式:默认只处理读请求
手动添加副本节点:
# 语法:redis-cli --cluster add-node <新节点> <目标主节点> --cluster-slave
redis-cli --cluster add-node 192.168.1.107:6379 192.168.1.101:6379 \
--cluster-slave \
-a ClusterPwd123
5.5 槽管理操作
1> 查看槽分配
redis-cli --cluster check 192.168.1.101:6379 -a ClusterPwd123
2> 重新分片
redis-cli --cluster reshard 192.168.1.101:6379 -a ClusterPwd123
交互过程:
-
输入要迁移的槽数量(如1000)
-
输入目标节点ID
-
输入源节点ID(或输入"all"从所有节点迁移)
-
输入"yes"开始迁移
3> 手动迁移单个槽
# 1. 设置槽状态
redis-cli -h 192.168.1.101 -a ClusterPwd123 CLUSTER SETSLOT 1213 MIGRATING <目标节点ID>
# 2. 迁移键值
redis-cli -h 192.168.1.101 -a ClusterPwd123 CLUSTER GETKEYSINSLOT 1213 100 | \
xargs -L 1 redis-cli -h 192.168.1.101 -a ClusterPwd123 MIGRATE 192.168.1.102 6379 "" 0 5000 KEYS
# 3. 完成迁移
redis-cli -h 192.168.1.102 -a ClusterPwd123 CLUSTER SETSLOT 1213 IMPORTING <源节点ID>
redis-cli -h 192.168.1.101 -a ClusterPwd123 CLUSTER SETSLOT 1213 NODE <目标节点ID>
redis-cli -h 192.168.1.102 -a ClusterPwd123 CLUSTER SETSLOT 1213 NODE <目标节点ID>
5.6 监控
# 检查集群健康状态、Slot 覆盖、主从配置
redis-cli --cluster check <host>:<port> -a <password>
# 查看槽分布
redis-cli --cluster info <host>:<port> -a <password>
# 内存分析
redis-cli -c --bigkeys -a <password>
# 查看集群整体状态(状态、Slot 分配、节点数等)
redis-cli -c -p <port> CLUSTER INFO
# 查看集群所有节点信息(ID、角色、状态、负责 Slot、主从关系等)最详细!
redis-cli -c -p <port> CLUSTER NODES
6、高可用性与数据安全分析
-
主节点故障: 通过主从复制 + 自动故障转移保障可用性(如前所述)。
-
从节点故障: 仅影响该副本的冗余。主节点继续服务。从节点恢复后重新同步。
-
多节点故障:
-
如果故障的主节点数超过集群剩余主节点数的一半 (
> N/2),集群将无法达成共识进行故障转移,进入FAIL状态,停止服务(如果cluster-require-full-coverage=yes)。 -
如果故障的是从节点,只要其主节点还在,不影响服务。
-
-
网络分区 (脑裂):
-
可能发生。Redis Cluster 使用 "大多数原则" 来防御脑裂:
-
只有拥有大多数主节点的分区才能进行故障转移和写入操作。
-
少数分区中的主节点会被标记为 FAIL,无法接受写入(即使它们在线)。
-
-
这可能导致少数分区中的数据丢失(如果客户端在少数分区中向旧主节点写入)。配置
min-replicas-to-write(虽然原生 Cluster 没有直接等同,但类似思想) 可以在一定程度上缓解,但 Redis Cluster 本身对此无完美解决方案。这是 CAP 中 AP 的选择。
-
7、适用场景与选择
-
选择 Redis Cluster 当:
-
数据量超过单机内存。
-
读写吞吐量超过单机 Redis 能力。
-
需要高可用性,且能容忍故障转移期间短暂不可用和极小概率的异步复制丢失。
-
应用程序能处理
MOVED/ASK重定向或使用支持 Cluster 的客户端/代理。
-
-
考虑其他方案当:
-
数据量小,单机 + 主从 + Sentinel 足够。
-
需要强一致性保证。
-
重度依赖多 Key 操作、事务、Lua 脚本且无法使用
Hash Tags。 -
需要跨地域部署(Redis Cluster 对网络延迟敏感,跨地域部署复杂,通常用主动复制如 CRDTs 或代理层处理)。
-
运维复杂度要求低(Cluster 比 Sentinel/单机复杂)。
-

1545

被折叠的 条评论
为什么被折叠?



