无论是主从复制模式,还是哨兵模式的主从复制模式,实际上都可以认为是“单机”部署的Redis实例,因为无论有多少从节点,主节点都只有一个,所有的数据写入都是写入主节点,从节点只是复制主节点而已。主从复制模式的缺点很明显,那就是无法水平扩展,如果数据量越来越大,对于单台机器的性能要求比较高。自Redis3.0开始,真正引入了Redis“集群”的概念,自此,Redis有了水平扩展的能力。
一、集群模式简介
与单机版Redis(包括主从复制模式)将整个数据库放在同一台服务器上的做法不同,Redis集群通过将数据库分散存储到多个节点上来平衡各个节点的负载压力。
具体来说,Redis集群会将整个数据库空间划分为16384个槽(slot)来实现数据分片(sharding),而集群中的各个主节点则会分别负责处理其中的一部分槽。当用户尝试将一个键存储到集群中时,客户端会先计算出键所属的槽,接着在记录集群节点槽分布的映射表中找出处理该槽的节点,最后再将键存储到相应的节点中,如下图所示:

Redis数据迁移的基本单位是槽(slot)。如果我们想新增一个节点,可以向集群发送几个命令,让集群把相应的槽以及槽内的数据迁移到新节点上;如果我们想删除一个节点,则集群会将被删除节点的槽迁移到其它节点上去。上述伴随着向集群新增或者删除节点导致槽迁移的过程被称为“重分片(reshard)”。无论是向集群中新增节点还是删除节点,整个重分片(reshard)过程都可以在线进行,Redis集群无须因此而停机。
集群模式有以下几个特点:
1.高可用。 Redis集群模式可为每个主节点配置副本节点,而且自带高可用功能,其机制和Sentinel类似,在主节点故障时自动实施故障转移。
2.高性能。 客户端发送的命令在绝大部分情况下都不需要实施转向,或者仅需要一次转向,因此在Redis集群中执行命令的性能与在单机Redis服务器上执行命令的性能非常接近,在海量数据的情况下Redis集群模式比Redis单机模式性能大幅度领先。
3.可扩展。 Redis集群通过槽机制让数据均匀分布或者根据权重分布在所有的节点上,在需要扩展时可以很方便的添加新的Redis实例,甚至不需要重启集群。
二、集群模式部署
接下来部署有十个Redis节点,包含五主五从的Redis集群,端口号设置从30001到30010。其架构图如下所示:

先创建10个文件夹,编号从30001到30010,然后将redis-server
以及redis.conf
文件挨个复制进去(redis编译参考:《CentOS安装Redis》),目录结构如下所示:
├── 30001
│ ├── redis.conf
│ └── redis-server
├── 30002
│ ├── redis.conf
│ └── redis-server
├── 30003
│ ├── redis.conf
│ └── redis-server
......
├── 30009
│ ├── redis.conf
│ └── redis-server
└── 30010
├── redis.conf
└── redis-server
1、配置文件修改
修改每个节点的redis.conf
文件:
port 30001 #修改该配置为正确的端口号,从30001到30010
daemonize yes #后台运行
logfile "server.log" #日志文件
dir "./" #工作目录设置在当前目录,即运行命令的目录,方便查看日志等
cluster-enabled yes #开启集群功能
cluster-config-file nodes-30001.conf #集群运行时配置文件,会自动创建
2、运行10个Redis服务
进入每个文件夹,使用命令./redis-server ./redis.conf
运行服务。
10个服务都启动起来之后,它们相互之间不知道彼此的存在,只是单纯的redis服务,并没有组成集群,此时要使用redis-cli工具创建集群让它们彼此关联起来。
3、创建集群
使用redis-cli运行如下命令创建集群:
./redis-cli --cluster create \ #创建集群命令
127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 127.0.0.1:30007 127.0.0.1:30008 127.0.0.1:30009 127.0.0.1:30010 \ #指定10个节点的ip和端口号
--cluster-replicas 1 #每个主节点1个副本
运行完会提示如下信息:
>>> Performing hash slots allocation on 10 nodes...
Master[0] -> Slots 0 - 3276
Master[1] -> Slots 3277 - 6553
Master[2] -> Slots 6554 - 9829
Master[3] -> Slots 9830 - 13106
Master[4] -> Slots 13107 - 16383
Adding replica 127.0.0.1:30007 to 127.0.0.1:30001
Adding replica 127.0.0.1:30008 to 127.0.0.1:30002
Adding replica 127.0.0.1:30009 to 127.0.0.1:30003
Adding replica 127.0.0.1:30010 to 127.0.0.1:30004
Adding replica 127.0.0.1:30006 to 127.0.0.1:30005
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: d85dc362370f81fe627305e9b246365bd51a02c2 127.0.0.1:30001
slots:[0-3276] (3277 slots) master
M: 9d148120512f85f29dd246e515243fd6019c7dcc 127.0.0.1:30002
slots:[3277-6553] (3277 slots) master
M: d5f1adb8939529abcfe24e657bd01787b7d7928c 127.0.0.1:30003
slots:[6554-9829] (3276 slots) master
M: 6d2c221d2ff15ae7fb9d5a5b7551ae5b493e791f 127.0.0.1:30004
slots:[9830-13106] (3277 slots) master
M: adbbff5339b083b1e4fb641951e6fb9085ffb25c 127.0.0.1:30005
slots:[13107-16383] (3277 slots) master
S: c33e236a553d9e658a0bb549d933567de5a756f8 127.0.0.1:30006
replicates 9d148120512f85f29dd246e515243fd6019c7dcc
S: 1728a0fb708c776303f25e33c0add3297332cd64 127.0.0.1:30007
replicates 6d2c221d2ff15ae7fb9d5a5b7551ae5b493e791f
S: dd7079d8486119fecc4e71f10fb705889ea8668b 127.0.0.1:30008
replicates d85dc362370f81fe627305e9b246365bd51a02c2
S: 0db0c21f6f9b075b887c0ef1548cf35cb0505b0b 127.0.0.1:30009
replicates adbbff5339b083b1e4fb641951e6fb9085ffb25c
S: 32661eb91b3be79b9a9d9ba873e1066bd64916a7 127.0.0.1:30010
replicates d5f1adb8939529abcfe24e657bd01787b7d7928c
Can I set the above configuration? (type 'yes' to accept):
以上是一段提示性信息,提示我们将要进行集群创建了,有哪些节点是主节点,每个主节点将分配多少个槽,每个主节点的副本节点是谁,主节点的id和副本节点的id是什么。。可以说尽可能详尽的告诉我们如果创建了集群,它将会是什么样子的。输入yes表示同意按照这个配置创建集群:
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.
>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: d85dc362370f81fe627305e9b246365bd51a02c2 127.0.0.1:30001
slots:[0-3276] (3277 slots) master
1 additional replica(s)
S: c33e236a553d9e658a0bb549d933567de5a756f8 127.0.0.1:30006
slots: (0 slots) slave
replicates 9d148120512f85f29dd246e515243fd6019c7dcc
S: 0db0c21f6f9b075b887c0ef1548cf35cb0505b0b 127.0.0.1:30009
slots: (0 slots) slave
replicates adbbff5339b083b1e4fb641951e6fb9085ffb25c
S: 32661eb91b3be79b9a9d9ba873e1066bd64916a7 127.0.0.1:30010
slots: (0 slots) slave
replicates d5f1adb8939529abcfe24e657bd01787b7d7928c
S: 1728a0fb708c776303f25e33c0add3297332cd64 127.0.0.1:30007
slots: (0 slots) slave
replicates 6d2c221d2ff15ae7fb9d5a5b7551ae5b493e791f
M: adbbff5339b083b1e4fb641951e6fb9085ffb25c 127.0.0.1:30005
slots:[13107-16383] (3277 slots) master
1 additional replica(s)
S: dd7079d8486119fecc4e71f10fb705889ea8668b 127.0.0.1:30008
slots: (0 slots) slave
replicates d85dc362370f81fe627305e9b246365bd51a02c2
M: 6d2c221d2ff15ae7fb9d5a5b7551ae5b493e791f 127.0.0.1:30004
slots:[9830-13106] (3277 slots) master
1 additional replica(s)
M: d5f1adb8939529abcfe24e657bd01787b7d7928c 127.0.0.1:30003
slots:[6554-9829] (3276 slots) master
1 additional replica(s)
M: 9d148120512f85f29dd246e515243fd6019c7dcc 127.0.0.1:30002
slots:[3277-6553] (3277 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
这样就创建成功了。
4、集群验证
我们用redis-cli工具验证,运行如下命令进入集群中的30001节点:
./redis-cli -c -p 30001
注意-c
参数的使用,表示是集群模式连接,如果没有这个参数,后续运行命令可能会报错。
运行命令
cluster info
查看输出:
cluster_state:ok #集群状态正常
cluster_slots_assigned:16384 #16384个槽均已分配
cluster_slots_ok:16384 #16384个槽状态都正常
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:10 #集群中有10个节点
cluster_size:5
cluster_current_epoch:10
cluster_my_epoch:1
cluster_stats_messages_ping_sent:13777
cluster_stats_messages_pong_sent:13518
cluster_stats_messages_fail_sent:12
cluster_stats_messages_sent:27307
cluster_stats_messages_ping_received:13518
cluster_stats_messages_pong_received:13767
cluster_stats_messages_fail_received:6
cluster_stats_messages_received:27291
从输出信息上来看,集群状态是正常的。
接下来运行如下命令查看所有节点信息:
cluster nodes
运行结果如下:

从输出上来看,5主5从10个节点都是正常的。还能看到一些主从关系信息,比如30010是30003的副本节点。
当然,可以运行如下命令确认一下:
CLUSTER REPLICAS d5f1adb8939529abcfe24e657bd01787b7d7928c
输出如下:

可以看到30010确实是30003的副本节点。
接下来看看其高可用机制是否生效,我们把30003主节点下掉,看看30010是否会成为主节点实现自动故障转移。
使用命令./redis-cli -p 30003
进入30003节点,运行命令shutdown
退出reids进程,之后查看30003的副本节点30010的日志:

从节点日志上来看,30010副本节点发现无法联系上30003主节点之后,发起了一次投票选举主节点,最后自己成为了新的主节点。
重新运行cluster nodes
命令:

可以看到30003节点已经被标记为下线状态,30010副本节点已经被提级为主节点了。
接下来再把30003节点启动起来,看看能否自动加入集群。
先看下其启动日志:

从启动日志上来看,30003节点启动之后把自己变成了30010的副本,并且发起了一次全量数据同步,30003节点应该变成了30010的副本。运行命令cluster nodes
命令查看:

可以看到重新启动起来的30003节点变成了30010的副本节点,这个和sentinel+主从复制模式中的处理策略是相同的。
至此,我们验证了集群已经成功搭建完成。
三、集群管理命令
与sentinel模式相似,cluster模式也有一套管理命令,使用./redis-cli -c -p 30001
进入集群任意一节点以后,使用命令cluster help
可以查看所有相关的命令:
127.0.0.1:30001> cluster help
1) CLUSTER <subcommand> [<arg> [value] [opt] ...]. Subcommands are:
2) ADDSLOTS <slot> [<slot> ...]
3) Assign slots to current node.
4) BUMPEPOCH
5) Advance the cluster config epoch.
6) COUNT-FAILURE-REPORTS <node-id>
7) Return number of failure reports for <node-id>.
8) COUNTKEYSINSLOT <slot>
9) Return the number of keys in <slot>.
10) DELSLOTS <slot> [<slot> ...]
11) Delete slots information from current node.
12) FAILOVER [FORCE|TAKEOVER]
13) Promote current replica node to being a master.
14) FORGET <node-id>
15) Remove a node from the cluster.
16) GETKEYSINSLOT <slot> <count>
17) Return key names stored by current node in a slot.
18) FLUSHSLOTS
19) Delete current node own slots information.
20) INFO
21) Return information about the cluster.
22) KEYSLOT <key>
23) Return the hash slot for <key>.
24) MEET <ip> <port> [<bus-port>]
25) Connect nodes into a working cluster.
26) MYID
27) Return the node id.
28) NODES
29) Return cluster configuration seen by node. Output format:
30) <id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ...
31) REPLICATE <node-id>
32) Configure current node as replica to <node-id>.
33) RESET [HARD|SOFT]
34) Reset current node (default: soft).
35) SET-CONFIG-EPOCH <epoch>
36) Set config epoch of current node.
37) SETSLOT <slot> (IMPORTING|MIGRATING|STABLE|NODE <node-id>)
38) Set slot state.
39) REPLICAS <node-id>
40) Return <node-id> replicas.
41) SAVECONFIG
42) Force saving cluster configuration on disk.
43) SLOTS
44) Return information about slots range mappings. Each range is made of:
45) start, end, master and replicas IP addresses, ports and ids
46) HELP
47) Prints this help.
差不多有20来个命令。下面逐一介绍各个命令。
cluster meet
cluster meet
命令用于将某个节点加入到当前集群(运行redis-cli所在的节点所属的集群)中,其完整命令如下所示:
cluster meet <ip> <port> [<bus-port>]
ip和port是待加入集群的ip地址和端口号,bus-port则似乎第一次见:bus-port是节点的集群通信端口号,默认值是<port>+10000
,比如待加入集群的节点端口号是30011,那么集群通信端口号就是30011+10000=40011。集群通信端口号不处理业务请求,仅用于集群内部通信,例如故障检测、配置更新、数据迁移等关键操作。
比如,我们在30001节点进入redis-cli,使用cluster meet命令加入一个新节点30011,将30011加入到集群中:
cluster meet 127.0.0.1 30011 #等价于 cluster meet 127.0.0.1 30011 40011
观察30011的日志:
2797:M 18 Jul 2025 18:20:51.333 # IP address for this node updated to 127.0.0.1
2797:M 18 Jul 2025 18:20:57.197 # Cluster state changed: ok
运行cluster nodes
命令观察新加入的节点:

可以看到30011作为master节点已经加入到了集群中。
cluster nodes
该命令用于查看集群内所有节点的相关信息,其完整命令:
cluster nodes
返回值是一个列表,形式如下所示:
<id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot>
各个字段的意思如下所示:
字段 | 信息项 | 意义 |
---|---|---|
<id> |
节点ID | 记录节点的运行ID。每个节点的运行ID在集群中都是唯一的,用户可以在执行集群操作时将其用作节点的标识符 |
<ip:port> |
地址和端口 | 记录节点的IP地址以及端口号。位于@符号左边的是节点的客户端端口,而位于@符号右边的则是节点的集群端口,前者用于与客户端通信,而后者则用于与集群中的其他节点通信 |
<flags> |
角色和状态 | 记录节点当前担任的角色以及节点目前所处的状态。表20-2记录了这个项可能出现的值,以及各个值代表的意思。当节点的角色和状态同时出现时,这个项会使用逗号去分隔多个值,比如显示myself,master表示这个节点是客户端目前正在连接的节点,并且它是一个主节点,而显示slave,fail?则表示这个节点是一个从节点,并且它正处于疑似下线状态 |
<master> |
主节点ID | 如果节点是一个从节点,那么这里显示的就是它正在复制的主节点的ID;如果节点本身就是一个主节点,那么它在这个项中只会显示一个-符号 |
<pings> |
发送PING消息的时间 | 节点最近一次向其他节点发送PING消息时的UNIX时间戳,格式为毫秒。如果该节点与其他节点的连接正常,并且它发送的PING消息也没有被阻塞,那么这个值将被设置为0 |
<pongs> |
收到PONG消息的时间 | 节点最近一次接收到其他节点发送的PONG消息时的UNIX时间戳,格式为毫秒。对于客户端正在连接的节点来说,这个项的值总是为0 |
<epoch> |
配置纪元 | 节点所处的配置纪元 |
<link> |
连接状态 | 节点集群总线的连接状态。connected表示连接正常,disconnected表示连接已断开 |
<slot> |
负责的槽 | 显示节点目前负责处理的槽以及这些槽所处的状态。表20-3记录了这个项可能出现的值以及各个值代表的含义。如果节点是一个从节点,或者是一个没有负责任何槽的主节点,那么这一项的值将为空 |
节点的角色和状态
值 | 说明 |
---|---|
myself | 这是客户端目前正在连接的节点 |
master | 这是一个主节点 |
slave | 这是一个从节点 |
fail? | 这个节点正处于疑似下线状态 |
fail | 这个节点已经下线 |
nofailover | 这个节点开启了 cluster-replica-no-failover 配置选项,带有这个标志的从节点即使在主服务器下线的情况下,也不会主动执行故障转移操作 |
handshake | 集群正在与这个节点握手,尚未确认它的状态 |
noaddr | 目前尚不清楚这个节点的具体地址 |
noflags | 目前尚不清楚这个节点担任的角色以及它所处的状态 |
槽的数字以及状态
槽的类型 | 打印方式 |
---|---|
连续的槽 | 每当CLUSTER NODES命令遇到连续的槽号时,它就会以start_slot-end_slot 格式打印节点负责的槽。比如,打印0-5460 代表节点负责从槽0直到槽5460在内的连续多个槽 |
不连续的槽 | 每当CLUSTER NODES命令遇到不连续的槽号时,就会单独地打印出不连续的槽号。比如,如果一个节点只负责了1、3、5这3个槽,那么命令在这个节点的槽号部分将打印出1 3 5 。因为CLUSTER NODES总是会将节点负责的每一个不连续槽号都打印出来,所以如果一个节点负责了大量不连续的槽,那么它的槽号部分可能会非常庞大 |
正在导入的槽 | 如果节点正在从另一个节点导入某个槽,那么CLUSTER NODES命令将以[slot_number-<-node_id] 的格式打印出被导入的槽以及该槽原来所在的节点。比如,打印[123-<-47b7ea54965875c3bf1316071584e842342c6fa3] 就代表节点正在从ID为47b7ea54965875c3bf1316071584e842342c6fa3的节点中导入槽123 |
正在迁移的槽 | 如果节点正在将自己的某个槽迁移至另一个节点,那么CLUSTER NODES命令将以[slot_number->-node_id] 格式打印出被迁移的槽以及该槽正在迁移的目标节点。比如,如果节点正在将自己的槽255迁移至ID为47b7ea54965875c3bf1316071584e842342c6fa3的节点,那么命令将打印出[255->-47b7ea54965875c3bf1316071584e842342c6fa3] |
以我们新增的30011节点为例:
79a28e2df5e13dd726c892c42ee35a15423b00fb 127.0.0.1:30011@40011 master - 0 1752834070495 0 connected
-
节点id是79a28e2df5e13dd726c892c42ee35a15423b00fb
-
节点ip和端口号是127.0.0.1:30011,集群通信端口号是40011
-
节点角色是master
-
节点是master节点,没有master,以-表示
-
由于节点连接正常,pings是0
-
收到PONG消息的时间是1752834070495
-
配置纪元是0
-
连接状态是connected,表示连接正常
-
没有slots字段,因为这个节点是新加入集群的节点,还没有分配槽。
cluster myid
该命令用于查看当前节点的运行ID。因为不少集群命令都需要使用节点的运行ID作为参数,所以当我们需要对正在连接的节点执行某个使用运行ID作为参数的操作时,就可以使用CLUSTER MYID命令快速地获得节点的ID。

cluster info
该命令用于查看与集群以及当前节点有关的状态信息。
以下是一个对节点30001执行CLUSTER INFO命令的示例:
127.0.0.1:30001> cluster info
cluster_state:ok #集群目前处于在线状态
cluster_slots_assigned:16384 #有16384个槽已经被指派
cluster_slots_ok:16384 #有16384个槽处于在线状态
cluster_slots_pfail:0 #没有槽处于疑似下线状态
cluster_slots_fail:0 #没有槽处于已下线状态
cluster_known_nodes:11 #集群包含11个节点
cluster_size:5 #集群中有5个节点被指派了槽
cluster_current_epoch:11 #集群当前所处的纪元为11
cluster_my_epoch:1 #节点当前所处的配置纪元为1
cluster_stats_messages_ping_sent:25124 #节点发送PING消息的数量
cluster_stats_messages_pong_sent:24625 #节点发送PONG消息的数量
cluster_stats_messages_meet_sent:1 #节点发送的meet消息的数量
cluster_stats_messages_fail_sent:20 #表示当前节点向其他节点发送的FAIL消息数量为20次。FAIL消息用于通知集群其他节点某个节点已被标记为下线(故障状态)
cluster_stats_messages_auth-ack_sent:1 #表示节点发送的认证确认消息(AUTH-ACK)数量为1次。这类消息用于集群节点间的身份验证响应,通常在安全认证场景中出现
cluster_stats_messages_sent:49771 #当前节点通过集群总线(node-to-node二进制总线)发送的所有消息总量,包括PING、PONG、FAIL等各类消息
cluster_stats_messages_ping_received:24625 #节点接收到的PING消息数量
cluster_stats_messages_pong_received:25114 #节点接收到的PONG消息数量
cluster_stats_messages_fail_received:7 #节点接收到的FAIL消息数量,表示其他节点通知本节点有7次关于某个节点故障的广播
cluster_stats_messages_auth-req_received:1 #节点接收到的认证请求消息(AUTH-REQ)数量,通常用于集群安全认证的初始化
cluster_stats_messages_received:49747 #节点通过集群总线接收的所有消息总量
从输出上来看,可以总结出来关于message的重要特点:
cluster_stats_messages_<type>_received
:表示某种类型消息接收的数量cluster_stats_messages_<type>_sent
:表示某种类型消息发送的数量
cluster forget
该命令用于从集群中移除某个节点,完整命令如下所示:
CLUSTER FORGET <node-id>
与CLUSTER MEET
命令引发的节点添加消息不一样,CLUSTER FORGET
命令引发的节点移除消息并不会通过Gossip协议传播至集群中的其他节点:当用户向一个节点发送CLUSTER FORGET
命令,让它去移除集群中的另一个节点时,接收到命令的节点只是暂时屏蔽了用户指定的节点,但这个被屏蔽的节点对于集群中的其他节点仍然是可见的。为此,要让集群真正地移除一个节点,用户必须向集群中的所有节点都发送相同的CLUSTER FORGET
命令,并且这一动作必须在60s之内完成,否则被暂时屏蔽的节点就会因为Gossip协议的作用而被重新添加到集群中。
可以使用redis-cli
工具对集群中所有节点发送相同的命令移除刚新增的30011节点:
./redis-cli --cluster call 127.0.0.1:30001 cluster forget 79a28e2df5e13dd726c892c42ee35a15423b00fb
cluster replicate
该命令用于将执行命令的当前节点转换为某个主节点的从节点,完整命令如下所示:
cluster replicate <node-id>
用户给定的主节点必须与当前节点位于相同的集群当中。此外,根据当前节点角色的不同,CLUSTER REPLICATE命令在执行时的情况也会有所不同:
- 如果当前节点是一个主节点,那么它必须是一个没有被指派任何槽的主节点,并且它的数据库中也不能有任何数据,这样它才可以转换成一个从节点。
- 如果当前节点已经是一个从节点,那么它将清空数据库中已有的数据,并开始复制用户给定的节点。
CLUSTER REPLICATE
命令在成功执行时将返回OK作为结果。与单机版本的REPLICAOF
命令一样,CLUSTER REPLICATE命令引发的复制操作也是异步执行的。
需要注意的是,该命令和单机版本的replicaof
不同,在使用单机版本的Redis时,用户可以让一个从服务器去复制另一个从服务器,以此来构建一系列链式复制的服务器;而Redis集群只允许节点对主节点而不是从节点进行复制,如果用户尝试使用CLUSTER REPLICATE命令让一个节点去复制一个从节点,那么命令将返回一个错误:

cluster replicas
该命令用于查询某个节点的所有从节点的信息,其完整命令如下所示:
cluster replicas <node-id>
该命令的输出可以认为是cluster nodes
命令的一部分,其输出表头是一样的。

cluster failover
该命令作用对象是从节点,用于对从节点强制故障转移,将从节点提升为主节点,其完整命令如下:
cluster failover [force|takeover]
默认情况下,CLUSTER FAILOVER
执行安全的主从切换,流程包括
- Slave通知Master停止处理客户端请求。
- Master返回当前复制偏移量(replication offset)。
- Slave等待数据同步完成后再晋升为Master。
- 新配置需获得集群中半数以上Master的投票认可
使用force和takeover可改变这个过程以适应不同情况下的集群恢复:
- force:当Master宕机无法响应Slave的协商请求,但集群中半数以上Master仍存活时使用;跳过与旧Master的数据同步协商(即跳过标准流程的前3步),直接发起选举,但仍需获得多数Master的投票认可才能晋升;在Master不可达但集群仍健康时加速切换,避免因等待超时而延长服务中断时间
- takeover:当半数以上Master节点故障(如机房断网),集群无法达成多数投票时使用;完全绕过选举机制,Slave直接生成最大的
config epoch
,强制接管所有Slot并广播新配置。其他节点收到后会无条件接受新主节点;可能导致配置冲突(如脑裂),仅用于极端故障恢复(如多活机房容灾)或测试环境。
cluster reset
该命令用于重置节点,完整命令如下所示:
cluster reset [hard|soft]
这个命令接受SOFT和HARD两个可选项作为参数,用于指定重置操作的具体行为(软重置和硬重置)。如果用户在执行CLUSTER RESET命令的时候没有显式地指定重置方式,那么命令默认将使用SOFT选项。
CLUSTER RESET命令在执行时,将对节点执行以下操作:
-
遗忘该节点已知的其他所有节点。
-
撤销指派给该节点的所有槽,并清空节点内部的槽-节点映射表。
-
如果执行该命令的节点是一个从节点,那么将它转换成一个主节点。
-
如果执行的是硬重置,那么为节点创建一个新的运行ID。
-
如果执行的是硬重置,那么将节点的纪元和配置纪元都设置为0。
-
通过集群节点配置文件的方式,将新的配置持久化到硬盘上。
需要注意的是,执行该命令的节点不能有数据,如果节点的数据库非空,那么该命令将执行失败。

cluster slots
该命令用于查看槽与节点之间的关联信息,完整命令如下所示:
cluster slots
命令会返回一个嵌套数组,数组中的每个项记录了一个槽范围(slot range)及其处理者的相关信息,其中包括
- 槽范围的起始槽。
- 槽范围的结束槽。
- 负责处理这些槽的主节点信息。
- 零个或任意多个主节点属下从节点的信息。
其中,每一项节点信息都由以下3项信息组成:
-
节点的IP地址。
-
节点的端口号。
-
节点的运行ID。
比如以下输出结果:
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 6554 #起始槽
2) (integer) 9829 #结束槽
3) 1) "127.0.0.1" #主节点ip地址
2) (integer) 30010 #主节点端口号
3) "32661eb91b3be79b9a9d9ba873e1066bd64916a7" #主节点id
4) 1) "127.0.0.1" #从节点ip地址
2) (integer) 30003 #从节点端口号
3) "d5f1adb8939529abcfe24e657bd01787b7d7928c" #从节点id
......
cluster keyslot
该命令用于查看键所属的槽,完整命令如下:
cluster keyslot <key>
比如30001节点有key f,想知道f存储在哪个槽了,可以使用该命令查询:

cluster addslots
该命令用于把指定的槽分配给当前节点,完整命令如下所示:
cluster addslots <slot> [<slot> ...]
需要注意的是,CLUSTER ADDSLOTS只能对尚未被指派的槽执行指派操作,如果用户给定的槽已经被指派,那么命令将报错:

cluster delslots
该命令用于撤销当前节点的槽指派,完整命令如下所示:
cluster delslots <slot> [<slot> ...]
比如,我们想要撤销30001节点的3168槽,可以使用如下命令:
cluster delslots 3168
运行cluster info命令,可以看到集群状态为失败状态,而且槽指派的数量本应是16384,现在是16383,减少了1

可以使用cluster slots
查看槽分配的详情。
需要注意的是,使用 CLUSTER DELSLOTS
命令删除槽指派后,对应槽中的数据不会立即被清除,但会变为不可访问状态。使用get命令获取key会提示如下信息:

如何恢复数据呢?
有两种情况,一是槽直接分配给原节点,这种情况可以使用cluster addslots
命令将槽分配下就可以了;另外一种情况是槽要迁移到别的节点上,这种情况需要使用cluster setslots
命令做槽迁移。
先用cluster addslots
尝试在原节点恢复槽分配:

正常获取到了f的key值,可以确定30001的3168槽已经恢复槽分配。
cluster setslot
该命令可以改变给定槽在节点中的状态,从而实现节点之间的槽迁移以及集群重分片。该命令完整格式如下所示:
cluster setslot <slot> (IMPORTING|MIGRATING|STABLE|NODE <node-id>)
该命令有四个子命令,通过四个命令的相互搭配,可以实现不同节点之间的槽迁移。我们接下来将30001(id为d85dc362370f81fe627305e9b246365bd51a02c2)节点上的3168槽迁移到30002(id为9d148120512f85f29dd246e515243fd6019c7dcc)节点上,看看如何操作。
在正式操作前,需要确保3168槽归30001节点所有,这点和addslots命令的要求完全相反的。
第一步:30001节点设置为MIGRATING状态
运行命令:
127.0.0.1:30001> cluster setslot 3168 MIGRATING d85dc362370f81fe627305e9b246365bd51a02c2
OK
运行完晒命令,30001节点将自己状态设置为“迁移中”状态,准备发送3168槽的数据。
第二步:30002节点设置为IMPORTING状态
运行命令:
127.0.0.1:30002> cluster setslot 3168 IMPORTING 9d148120512f85f29dd246e515243fd6019c7dcc
OK
运行命令后,节点30002的状态被设置为“导入中”状态,准备接收3168槽的数据。
第三步:key迁移
获取3168槽中的key:
127.0.0.1:30001> CLUSTER GETKEYSINSLOT 3168 10
1) "f"
127.0.0.1:30001>
可以看到只有一个key。
然后使用migrate命令将key迁移到30002节点:
127.0.0.1:30001> migrate 127.0.0.1 30002 "" 0 3000 keys f
OK
127.0.0.1:30001>
第四步:将槽指派给节点
迁移完成数据以后,在集群中任意节点将3168槽指派给30002节点。
cluster setslot 3168 node 9d148120512f85f29dd246e515243fd6019c7dcc
集群的其中一个节点在执行了NODE子命令之后,对给定槽的新指派信息将被传播至整个集群,目标节点在接收到这一信息之后将移除给定槽的“导入中”状态,而源节点在接收到这一信息之后将移除给定槽的“迁移中”状态。
30001节点再次查询f的键值,将会重定向到30002节点:
127.0.0.1:30001> get f
-> Redirected to slot [3168] located at 127.0.0.1:30002
"6"
127.0.0.1:30002>
可以看到,使用cluster setslot
命令迁移槽节点是比较麻烦的,为了精简这个步骤,可以使用cluster-cli
的工具命令,一个命令就可以解决了:
./redis-cli --cluster reshard 127.0.0.1:30002 --cluster-from 9d148120512f85f29dd246e515243fd6019c7dcc --cluster-to d85dc362370f81fe627305e9b246365bd51a02c2 --cluster-slots 1
该命令的确定是不能精准控制哪几个槽的迁移,只能确定迁移的数量。部分输出如下:

可以看到挑选需要迁移的节点也可能是有策略的,并没有破坏了其它连续的槽节点,而是挑选了单独的3168槽迁移。
关于redis-cli工具的具体使用,后续会有介绍。
cluster keyslot
该命令用于查看键所属的槽,完整命令如下所示:
cluster keyslot <key>
比如我们想查看f键所属的槽,可以运行如下命令:
127.0.0.1:30001> CLUSTER KEYSLOT f
(integer) 3168
127.0.0.1:30001>
cluster countkeysinslot
该命令用于查询槽包含的键数量,完整命令如下所示:
cluster countkeysinslot <slot>
举例,我们想查看3168槽中包含的键数量,可以使用如下命令:
127.0.0.1:30002> cluster countkeysinslot 3168
(integer) 1
127.0.0.1:30002>
注意运行该命令必须在3168槽所属的节点上运行,否则会返回0。
cluster getkeysinslot
该命令用于获取槽包含的键,完整命令如下所示:
cluster getkeysinslot <slot> <count>
比如我想从3168槽中获取10条数据,可以运行如下命令:
127.0.0.1:30002> cluster getkeysinslot 3168 10
1) "f"
127.0.0.1:30002>
注意该命令必须在3168槽所属的节点上运行,否则查询不到数据。
cluster flushslots
该命令用于撤销对节点的所有槽指派,相当于对节点上的每个槽执行cluster delslots
命令。完整命令如下所示:
cluster flushslots
用户在执行CLUSTER FLUSHSLOTS命令之前,必须确保节点的数据库为空,否则节点将拒绝执行命令并返回一个错误。
127.0.0.1:30002> cluster flushslots
(error) ERR DB must be empty to perform CLUSTER FLUSHSLOTS.
四、注意事项
1、集群模式对部分命令的影响
由于集群模式比较特殊,每个节点分管部分槽,这导致每个节点都不知道全部节点的key都是什么,所以使用keys
命令只能查看自己节点的键。
127.0.0.1:30001> set a 1
-> Redirected to slot [15495] located at 127.0.0.1:30005
OK
127.0.0.1:30005> set b 2
-> Redirected to slot [3300] located at 127.0.0.1:30002
OK
127.0.0.1:30002> set c 3
-> Redirected to slot [7365] located at 127.0.0.1:30010
OK
127.0.0.1:30010> set f 4
-> Redirected to slot [3168] located at 127.0.0.1:30001
OK
127.0.0.1:30001> keys *
1) "f"
127.0.0.1:30001>
同样的,cluster countkeysinslot
以及cluster getkeysinslot
命令都有类似的问题,需要注意。
2、强制键放在同槽的方法
在默认情况下,Redis将根据用户输入的整个键计算出该键所属的槽,然后将键存储到相应的槽中,但是有些时候,需要将键放到同一个槽中,这时候可以使用散列标签实现该功能。
散列标签功能会找出键中第一个被大括号{}包围并且非空的字符串子串(sub string),然后根据子串计算出该键所属的槽。这样一来,即使两个键原本不属于同一个槽,但只要它们拥有相同的被包围子串,那么程序计算出的散列值就是一样的,因此Redis集群就会把它们存储到同一个槽中。
举个例子,默认情况下我们存储user:1
以及user:2
结果如下所示:
127.0.0.1:30001> set user:1 1
-> Redirected to slot [10778] located at 127.0.0.1:30004
OK
127.0.0.1:30004> set user:2 2
-> Redirected to slot [6777] located at 127.0.0.1:30010
OK
可以看到两个键被分别存储到了3004节点以及30010节点上不同的槽。
但是如果使用散列标签方式,可以让user开头的key都存到同一个槽中:
127.0.0.1:30010> set {user}:1 1
-> Redirected to slot [5474] located at 127.0.0.1:30002
OK
127.0.0.1:30002> set {user}:2 2
OK
127.0.0.1:30002> cluster getkeysinslot 5474 10
1) "{user}:1"
2) "{user}:2"
可以看到两个键都被存到了30002节点上的5474槽位。
END.
注意:本文归作者所有,未经作者允许,不得转载