Redis集群是Redis 3.0版本开始正式引入的功能,它给用户带来了在线扩展Redis系统读写性能的能力,而Redis 5.0更是在集群原有功能的基础上,进一步添加了更多新功能,并且对原有功能做了相当多的优化,使得整个集群系统更简单、易用和高效。
ClusterNode结构保存了节点的当前状态,如节点创建时间、节点名称、节点、当前配置的纪元、节点的IP地址和端口号等。
每个节点将使用clusternode结构来记录自己的状态,并为集群中的所有其他节点(包括主节点和从节点)创建相应的clusternode结构,以此记录其他节点的状态。
cluster.h
typedef struct clusterNode {
mstime_t ctime; /* Node object creation time. */ // 创建节点的时间
char name[CLUSTER_NAMELEN]; /* Node name, hex string, sha1-size */ // 节点的名字,由40个十六进制字符组成
int flags; /* CLUSTER_NODE_... */ // 节点标识,如主节点、从节点以及在线、下线
uint64_t configEpoch; /* Last configEpoch observed for this node */ // 节点当前的配置纪元,用于实现故障转移
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */ // CLUSTER_SLOTS = 16384
sds slots_info; /* Slots info represented by string. */
int numslots; /* Number of slots handled by this node */ // 记录节点负责处理的槽的数量
int numslaves; /* Number of slave nodes, if this is a master */
...
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */ // 节点的ip地址
int port; /* Latest known clients port (TLS or plain). */ // 节点的端口号
int pport; /* Latest known clients plaintext port. Only used
if the main clients port is for TLS. */
int cport; /* Latest known cluster port of this node. */
clusterLink *link; /* TCP/IP link with this node */ // 保存连接节点所需的有关信息
list *fail_reports; /* List of nodes signaling this as failing */
} clusterNode;
clusterNode结构的link属性是一个clusterLink结构,该节点保存了连接节点所需的有关信息,比如套接字描述符、输入缓冲区和输出缓冲区:
/* clusterLink encapsulates everything needed to talk with a remote node. */
typedef struct clusterLink {
mstime_t ctime; /* Link creation time */ // 连接的创建时间
connection *conn; /* Connection to remote node */
sds sndbuf; /* Packet send buffer */ // 输出缓冲区,保存着等待发送给其他节点的消息(message)
char *rcvbuf; /* Packet reception buffer */ // 输入缓冲区,保存着从其他节点接收到的消息。
size_t rcvbuf_len; /* Used size of rcvbuf */
size_t rcvbuf_alloc; /* Allocated size of rcvbuf */
struct clusterNode *node; /* Node related to this link if any, or NULL */ // 与此连接相关的节点,如果没有的话就为null
} clusterLink;
最后,每个节点都保存着一个clusterstate结构,这个结构记录当前节点的视角下,集群目前的所处状态,如集群是在线还是离线,集群有多少个节点,集群当前配置纪元等
typedef struct clusterState {
clusterNode *myself; /* This node */ // 指向当前节点的指针
uint64_t currentEpoch; // 集群当前的配置纪元,用于实现故障转移
int state; /* CLUSTER_OK, CLUSTER_FAIL, ... */ // 集群当前的状态
int size; /* Num of master nodes with at least one slot */ // 集群中至少处理着一个槽的节点的数量
dict *nodes; /* Hash table of name -> clusterNode structures */ // 集群节点名单,字典的键为节点的名字,字典的值为节点对应的clusterNode结构
dict *nodes_black_list; /* Nodes we don't re-add for a few seconds. */
...
} clusterState;
Redis集群与单机版Redis服务器一样,也提供了主从复制功能。在Redis集群中,各个Redis服务器被称为节点(node),其中主节点(master node)负责处理客户端发送的读写命令请求,而从节点(replica/slave node)则负责对主节点进行复制。
除了复制功能之外,Redis集群还提供了类似于单机版Redis Sentinel的功能,以此来为集群提供高可用特性。简单来说,集群中的各个节点将互相监视各自的运行状况,并在某个主节点下线时,通过提升该节点的从节点为新主节点来继续提供服务。
与单机版Redis将整个数据库放在同一台服务器上的做法不同,Redis集群通过将数据库分散存储到多个节点上来平衡各个节点的负载压力。
具体来说,Redis集群会将整个数据库空间划分为16384个槽(slot)来实现数据分片(sharding),而集群中的各个主节点则会分别负责处理其中的一部分槽。当用户尝试将一个键存储到集群中时,客户端会先计算出键所属的槽,接着在记录集群节点槽分布的映射表中找出处理该槽的节点,最后再将键存储到相应的节点中.
当用户想要向集群添加新节点时,只需要向Redis集群发送几条简单的命令,集群就会将相应的槽以及槽中存储的数据迁移至新节点。与此类似,当用户想要从集群中移除已存在的节点时,被移除的节点也会将自己负责处理的槽以及槽中数据转交给集群中的其他节点负责。最重要的是,无论是向集群添加新节点还是从集群中移除已有节点,整个重分片(reshard)过程都可以在线进行,Redis集群无须因此而停机。
Redis集群采用无代理模式,客户端发送的所有命令都会直接交由节点执行,并且对于经过优化的集群客户端来说,客户端发送的命令在绝大部分情况下都不需要实施转向,或者仅需要一次转向,因此在Redis集群中执行命令的性能与在单机Redis服务器上执行命令的性能非常接近。
除了节点之间互通信息带来的性能损耗之外,单个Redis集群节点处理命令请求的性能与单个Redis服务器处理命令请求的性能几乎别无二致。从理论上来讲,集群每增加一倍数量的主节点,集群对于命令请求的处理性能就会提高一倍。
Redis为集群提供了丰富的工具和命令,用户可以通过工具快速创建测试用集群,并在之后通过命令行命令或者Redis内置的集群命令管理和控制集群。与此同时,因为Redis集群只需要非常少的配置信息,所以即使你打算手动构建自己的集群,实施起来应该也不会遇到什么困难。
要使用Redis集群,首先要做的就是搭建一个完整的集群,Redis为此提供了两种方法:一种是使用源码附带的集群自动搭建程序,另一种则是使用配置文件手动搭建集群,接下来将对这两种搭建方式做详细的介绍。
Redis在它的源码中附带了集群自动搭建程序create-cluster,这个程序可以快速构建起一个完整可用的集群以供用户测试。
create-cluster程序位于源码的utils/create-cluster/create-cluster位置,通过不给定任何参数来执行它,我们可以看到该程序的具体用法:
$ ./create-cluster
Usage: ./create-cluster [start|create|stop|watch|tail|clean]
start -- Launch Redis Cluster instances.
create -- Create a cluster using redis-cli --cluster create.
stop -- Stop Redis Cluster instances.
watch -- Show CLUSTER NODES output (first 30 lines) of first node.
tail <id> -- Run tail -f of instance at base port + ID.
clean -- Remove all instances data, logs, configs.
clean-logs -- Remove just instances logs.
首先,我们可以通过执行start命令来创建出6个节点,这6个节点的IP地址都为本机,而端口号则为30001~30006:
$ ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006
接着,我们需要使用create命令,把上述6个节点组合成一个集群,其中包含3个主节点和3个从节点:
$ ./create-cluster create
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30004 to 127.0.0.1:30001
Adding replica 127.0.0.1:30005 to 127.0.0.1:30002
Adding replica 127.0.0.1:30006 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
M: b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
M: a80b64eedcd15329bc0dc7b71652ecddccf6afe8 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
S: ab0b79f233efa0afa467d9ef1700fe5b24154992 127.0.0.1:30004
replicates a80b64eedcd15329bc0dc7b71652ecddccf6afe8
S: f584b888fcc0e7648bd838cb3b0e2d1915ac0ad7 127.0.0.1:30005
replicates 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f
S: 262acdf22f4adb6a20b8116982f2940890693d0b 127.0.0.1:30006
replicates b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a
Can I set the above configuration? (type 'yes' to accept):
create命令会根据现有的节点制定出一个相应的角色和槽分配计划,然后询问你的意见。以上面打印出的计划为例:
如果你同意程序给出的这个分配计划,那么只需要输入yes并按下Enter键,程序就会按计划组建集群了:
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: 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 262acdf22f4adb6a20b8116982f2940890693d0b 127.0.0.1:30006
slots: (0 slots) slave
replicates b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a
M: a80b64eedcd15329bc0dc7b71652ecddccf6afe8 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
M: b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: f584b888fcc0e7648bd838cb3b0e2d1915ac0ad7 127.0.0.1:30005
slots: (0 slots) slave
replicates 9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f
S: ab0b79f233efa0afa467d9ef1700fe5b24154992 127.0.0.1:30004
slots: (0 slots) slave
replicates a80b64eedcd15329bc0dc7b71652ecddccf6afe8
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
在成功构建起集群之后,我们就可以使用客户端来连接并使用集群了,要做到这一点,最简单的就是使用Redis附带的redis-cli客户端。在连接集群节点而不是单机Redis服务器时,我们需要向redis-cli提供c(cluster,集群)参数以指示客户端进入集群模式,并通过h(host,主机地址)参数或p(port,端口号)参数指定集群中的某个节点作为入口:
-- 连接本机端口30001上的集群节点,并向它发送PING命令
$ redis-cli -c -p 30001
127.0.0.1:30001> PING
PONG
如果接收到命令请求的节点并非负责处理命令所指键的节点,那么客户端将根据节点提示的转向信息再次向正确的节点发送命令请求,Redis集群把这个动作称为“转向”(redirect)。
-- 发送至节点30001的命令请求被转向节点30002
127.0.0.1:30001> SET msg "hi"
-> Redirected to slot [6257] located at 127.0.0.1:30002
OK
如果客户端发送的命令请求正好是由接收命令请求的节点负责处理,那么节点将直接向客户端返回命令执行结果,就像平时向单机服务器发送命令请求一样:
-- 因为键number所属的槽7743正好是由节点30002负责
-- 所以命令请求可以在不转向的情况下直接执行
127.0.0.1:30002> SET number 10086
OK
最后,在使用完这个测试集群之后,我们可以通过以下命令关闭集群并清理各个集群节点的相关信息:
$ ./create-cluster stop
Stopping 30001
Stopping 30002
Stopping 30003
Stopping 30004
Stopping 30005
Stopping 30006
$ ./create-cluster clean -- 清理完,下次才能继续使用
使用create-cluster程序快速搭建Redis集群虽然非常方便,但是由于该程序搭建的Redis集群不具备配置文件、主从节点数量固定以及槽分配模式固定等原因,这些快速搭建的集群通常只能够用于测试,但是无法应用在实际的生产环境中。为了搭建真正能够在生产环境中使用的Redis集群,我们需要创建相应的配置文件,并使用集群管理命令对集群进行配置和管理。
为了保证集群的各项功能可以正常运转,一个集群至少需要3个主节点和3个从节点。不过为了与之前使用create-cluster程序搭建的集群区别开来,这次我们将搭建一个由3个主节点和3个从节点组成的Redis集群。
为此,我们需要先创建出6个文件夹,用于存放相应节点的数据以及配置文件:
$ mkdir my-cluster
$ cd my-cluster/
$ mkdir node1 node2 node3 node4 node5 node6
接着,我们需要在每个节点文件夹中创建一个包含以下内容的redis.conf配置文件:
# 端口号
port 7000
# 后台启动
daemonize yes
# 开启集群
cluster-enabled yes
#集群节点配置文件
cluster-config-file nodes-7000.conf
# 集群连接超时时间
cluster-node-timeout 5000
# 进程pid的文件位置
pidfile /var/run/redis-7000.pid
# 开启aof
appendonly yes
# aof文件路径
appendfilename "appendonly-7000.aof"
# rdb文件路径
dbfilename dump-7000.rdb
其中,cluster-enabled选项的值为yes表示将Redis实例设置成集群节点而不是单机服务器,而port选项则用于为每个节点设置不同的端口号。在本例中,我们为6个节点分别设置了从7000~7005的端口号。
在为每个节点都设置好相应的配置文件之后,我们需要通过以下命令,陆续启动各个文件夹中的集群节点:
redis> ./redis-server my-cluster/node1/redis.conf
redis> ./redis-server my-cluster/node2/redis.conf
redis> ./redis-server my-cluster/node3/redis.conf
redis> ./redis-server my-cluster/node4/redis.conf
redis> ./redis-server my-cluster/node5/redis.conf
redis> ./redis-server my-cluster/node6/redis.conf
这个时候我们查看一下进程看启动情况
ps -ef | grep redis
进程状态如下:
root 1731 1 1 18:21 ? 00:00:49 bin/redis-server *:7000 [cluster]
root 1733 1 0 18:21 ? 00:00:29 bin/redis-server *:7001 [cluster]
root 1735 1 0 18:21 ? 00:00:08 bin/redis-server *:7002 [cluster]
root 1743 1 0 18:21 ? 00:00:26 bin/redis-server *:7003 [cluster]
root 1745 1 0 18:21 ? 00:00:13 bin/redis-server *:7004 [cluster]
root 1749 1 0 18:21 ? 00:00:08 bin/redis-server *:7005 [cluster]
cluster选项的create子命令允许用户根据已有的节点创建出一个集群。用户只需要在命令中依次给出各个节点的IP地址和端口号,命令就会将它们聚合到同一个集群中,并根据节点的数量将槽平均地指派给它们负责:
create <ip1>:<port1> ... <ipN>:<portN>
如果用户想要创建出一个包含3个节点的集群,可以执行以下命令:
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
用户还可以在创建集群的同时,决定要为每个主节点配备多少个从节点:
--cluster-replicas <num>
虽然我们已经启动了6个集群节点,但由于这些集群并未互联互通,所以它们都只在它们各自的集群之内。因此,我们接下来要做的就是连接这6个集群节点并为它们分配槽,这可以通过执行以下命令来完成:
$ redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
redis-cli --cluster是Redis客户端附带的集群管理工具,它的create子命令接受任意多个节点的IP地址和端口号作为参数,然后使用这些节点组建起一个Redis集群。create子命令允许使用多个可选参数,其中可选参数cluster-replicas用于指定集群中每个主节点的从节点数量。在上面的命令调用中,该参数的值为1,这表示我们想要为每个主节点设置一个从节点。
在执行上述命令之后,create子命令将制定出以下节点角色和槽分配计划:
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7004 to 127.0.0.1:7000
Adding replica 127.0.0.1:7005 to 127.0.0.1:7001
Adding replica 127.0.0.1:7003 to 127.0.0.1:7002
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
M: f57803665706ab636656d21e9563a9da1b956117 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
S: 98af0971457926216f3144ee2ff1c4704476b5d9 127.0.0.1:7003
replicates 6300020648a3a9020f0a99dcbd7ae51d7eee8140
S: 8455e2d2debeee6888133613effef6abf0f1513f 127.0.0.1:7004
replicates e8dc4916132f65b727bc6dc8aa88a42618a608c0
S: ab287d7a2f7f5b477a48de6e58bfdc2a927ce4f3 127.0.0.1:7005
replicates f57803665706ab636656d21e9563a9da1b956117
从这份计划可以看出,命令打算把节点7000~7002设置为主节点,并把16384个槽平均分配给这3个节点负责,至于节点7003~7005则分别指派给了3个主节点作为从节点。在输入yes并按下Enter键之后,create命令就会执行实际的分配和指派工作:
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:7000)
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 98af0971457926216f3144ee2ff1c4704476b5d9 127.0.0.1:7003
slots: (0 slots) slave
replicates 6300020648a3a9020f0a99dcbd7ae51d7eee8140
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: f57803665706ab636656d21e9563a9da1b956117 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: ab287d7a2f7f5b477a48de6e58bfdc2a927ce4f3 127.0.0.1:7005
slots: (0 slots) slave
replicates f57803665706ab636656d21e9563a9da1b956117
S: 8455e2d2debeee6888133613effef6abf0f1513f 127.0.0.1:7004
slots: (0 slots) slave
replicates e8dc4916132f65b727bc6dc8aa88a42618a608c0
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
现在整个集群已经设置完毕,并且16384个槽也已经全部分配给了各个节点。
拥有集群之后,用户就可以通过cluster选项的info子命令查看集群的相关信息。为了找到指定的节点,用户需要向命令提供集群中任意一个节点的地址作为参数:
info <ip>:<port>
命令返回的信息包括:
redis-cli --cluster info 127.0.0.1:7001
127.0.0.1:7001 (e8dc4916...) -> 3 keys | 5462 slots | 1 slaves.
127.0.0.1:7000 (63000206...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:7002 (f5780366...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 5 keys in 3 masters.
0.00 keys per slot on average.
从命令返回的结果可以看到,节点30001所在的集群包含了3个主节点,以主节点127.0.0.1:7001为例:
命令结果的最后两行记录了集群的总体存储情况:整个集群总共拥有3个主节点,这些主节点一共存储了5个键,平均每个槽只存储了0个键。
通过cluster选项的check子命令,用户可以检查集群的配置是否正确,以及全部16384个槽是否已经全部指派给了主节点。与info子命令一样,check子命令也接受集群其中一个节点的地址作为参数:
check <ip>:<port>
对于一个正常运行的集群,对其执行check子命令将得到一切正常的结果:
$ redis-cli --cluster check 127.0.0.1:7001
127.0.0.1:7001 (e8dc4916...) -> 3 keys | 5462 slots | 1 slaves.
127.0.0.1:7000 (63000206...) -> 2 keys | 5461 slots | 1 slaves.
127.0.0.1:7002 (f5780366...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 5 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: ab287d7a2f7f5b477a48de6e58bfdc2a927ce4f3 127.0.0.1:7005
slots: (0 slots) slave
replicates f57803665706ab636656d21e9563a9da1b956117
S: 98af0971457926216f3144ee2ff1c4704476b5d9 127.0.0.1:7003
slots: (0 slots) slave
replicates 6300020648a3a9020f0a99dcbd7ae51d7eee8140
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: f57803665706ab636656d21e9563a9da1b956117 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8455e2d2debeee6888133613effef6abf0f1513f 127.0.0.1:7004
slots: (0 slots) slave
replicates e8dc4916132f65b727bc6dc8aa88a42618a608c0
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
在默认情况下,Redis将根据用户输入的整个键计算出该键所属的槽,然后将键存储到相应的槽中。但是在某些情况下,出于性能方面的考虑,或者为了在同一个节点上对多个相关联的键执行批量操作,我们也会想要将一些原本不属于同一个槽的键放到相同的槽里面。
为了满足这一需求,Redis为用户提供了散列标签(hash tag)功能,该功能会找出键中第一个被大括号{}包围并且非空的字符串子串(substring),然后根据子串计算出该键所属的槽。这样一来,即使两个键原本不属于同一个槽,但只要它们拥有相同的被包围子串,那么程序计算出的散列值就是一样的,因此Redis集群就会把它们存储到同一个槽中。
-- 使用CLUSTER KEYSLOT命令查看给定键所属的槽
127.0.0.1:7002> CLUSTER KEYSLOT user::10086
(integer) 14982 -- 该键属于14982槽
127.0.0.1:7002> CLUSTER KEYSLOT user::10087
(integer) 10919 -- 该键属于10919槽
但如果我们对这两个键使用散列标签功能,即使用大括号去包围它们的user子串,让Redis集群只根据这一子串计算键的散列值,那么这两个键将被分配至同一个槽:
127.0.0.1:7002> CLUSTER KEYSLOT {user}::10086
(integer) 5474
127.0.0.1:7002> CLUSTER KEYSLOT {user}::10087
(integer) 5474
为了验证这一点,我们可以实际地对这两个键执行设置操作。在未使用散列标签功能时,对user::10086键的设置将被转向至节点7003所属的槽14982中,而对user::10087键的设置将被转向至节点7002所属的槽10919中:
127.0.0.1:7001> HMSET user::10086 name peter age 28 job programmer
-> Redirected to slot [14982] located at 127.0.0.1:7003
OK
127.0.0.1:7003> HMSET user::10087 name jack age 34 job writer
-> Redirected to slot [10919] located at 127.0.0.1:7002
OK
但是在使用散列标签功能的情况下,针对{user}::10086和{user}::10087两个键的设置操作将不会引发转向,这是因为它们都被放置到了节点7002所属的槽5474中:
127.0.0.1:7002> HMSET {user}::10086 name peter age 28 job programmer
OK
127.0.0.1:7002> HMSET {user}::10087 name jack age 34 job writer
OK
集群的从节点在默认情况下只会对主节点进行复制,但是不会处理客户端发送的任何命令请求:每当从节点接收到命令请求的时候,它只会向客户端发送转向消息,引导客户端向某个主节点重新发送命令请求。
127.0.0.1:7005> GET num
-> Redirected to slot [2765] located at 127.0.0.1:7001
"10086"
127.0.0.1:7001>
用户可以通过执行以下命令,让客户端临时获得在从服务器上执行读命令的权限:
READONLY
通过执行READONLY命令,我们可以让客户端临时获得在7005上执行读命令的权限,这样一来,针对num键的GET命令请求就不会被转向至节点7001,而是直接在节点7005上执行:
127.0.0.1:7005> READONLY
OK
127.0.0.1:7005> GET num
"10086"
READONLY命令只对执行了该命令的客户端有效,它并不影响正在访问相同从节点的其他客户端。
在使用READONLY命令打开客户端对从节点的读命令执行权限之后,我们可以通过执行以下命令重新关闭该权限:
READWRITE
这个命令在执行完毕之后将返回OK作为结果。这样一来,执行了该命令的客户端将不能再对从服务器执行读命令。
127.0.0.1:7005> READWRITE
OK
127.0.0.1:7005> GET num
-> Redirected to slot [2765] located at 127.0.0.1:7001
"10086"
127.0.0.1:7001>
$ ./redis-cli --cluster help
Cluster Manager Commands:
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check host:port
--cluster-search-multiple-owners
info host:port
fix host:port
--cluster-search-multiple-owners
--cluster-fix-with-unreachable-masters
reshard host:port
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance host:port
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
--cluster-only-masters
--cluster-only-replicas
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-from-user <arg>
--cluster-from-pass <arg>
--cluster-from-askpass
--cluster-copy
--cluster-replace
backup host:port backup_directory
help
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
Cluster Manager Options:
--cluster-yes Automatic yes to cluster commands prompts
当集群在重分片、负载均衡或者槽迁移的过程中出现错误时,执行cluster选项的fix子命令,可以让操作涉及的槽重新回到正常状态:
fix <ip>:<port>
fix命令会检查各个节点中处于“导入中”和“迁移中”状态的槽,并根据情况,将槽迁移至更合理的一方。
$ ./redis-cli --cluster fix 127.0.0.1:7001
127.0.0.1:7001 (e8dc4916...) -> 4 keys | 5462 slots | 1 slaves.
127.0.0.1:7000 (63000206...) -> 3 keys | 5461 slots | 1 slaves.
127.0.0.1:7002 (f5780366...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 8 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: ab287d7a2f7f5b477a48de6e58bfdc2a927ce4f3 127.0.0.1:7005
slots: (0 slots) slave
replicates f57803665706ab636656d21e9563a9da1b956117
S: 98af0971457926216f3144ee2ff1c4704476b5d9 127.0.0.1:7003
slots: (0 slots) slave
replicates 6300020648a3a9020f0a99dcbd7ae51d7eee8140
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: f57803665706ab636656d21e9563a9da1b956117 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8455e2d2debeee6888133613effef6abf0f1513f 127.0.0.1:7004
slots: (0 slots) slave
replicates e8dc4916132f65b727bc6dc8aa88a42618a608c0
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
如果fix命令在检查集群之后没有发现任何异常,那么它将不做其他动作,直接退出。
通过cluster选项的reshard子命令,用户可以将指定数量的槽从原节点迁移至目标节点,被迁移的槽将交由后者负责,并且槽中已有的数据也会陆续从原节点转移至目标节点:
reshard <ip>:<port>
--cluster-from <id> # 源节点的ID
--cluster-to <id> # 目标节点的ID
--cluster-slots <num> # 需要迁移的槽数量
--cluster-yes # 直接确认
--cluster-timeout <time> # 迁移的最大时限
--cluster-pipeline <yes/no> # 是否使用流水线
我们将其中的10个槽迁移至节点7000,那么可以执行以下命令:
$ ./redis-cli --cluster reshard 127.0.0.1:7001 --cluster-from e8dc4916132f65b727bc6dc8aa88a42618a608c0 --cluster-to 6300020648a3a9020f0a99dcbd7ae51d7eee8140 --cluster-slots 10
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: ab287d7a2f7f5b477a48de6e58bfdc2a927ce4f3 127.0.0.1:7005
slots: (0 slots) slave
replicates f57803665706ab636656d21e9563a9da1b956117
S: 98af0971457926216f3144ee2ff1c4704476b5d9 127.0.0.1:7003
slots: (0 slots) slave
replicates 6300020648a3a9020f0a99dcbd7ae51d7eee8140
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: f57803665706ab636656d21e9563a9da1b956117 127.0.0.1:7002
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8455e2d2debeee6888133613effef6abf0f1513f 127.0.0.1:7004
slots: (0 slots) slave
replicates e8dc4916132f65b727bc6dc8aa88a42618a608c0
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Ready to move 10 slots.
Source nodes:
M: e8dc4916132f65b727bc6dc8aa88a42618a608c0 127.0.0.1:7001
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
Destination node:
M: 6300020648a3a9020f0a99dcbd7ae51d7eee8140 127.0.0.1:7000
slots:[0-5460] (5461 slots) master
1 additional replica(s)
Resharding plan:
Moving slot 5461 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5462 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5463 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5464 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5465 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5466 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5467 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5468 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5469 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Moving slot 5470 from e8dc4916132f65b727bc6dc8aa88a42618a608c0
Do you want to proceed with the proposed reshard plan (yes/no)? yes
可以看到,reshard命令会先用check子命令检查一次集群,确保集群和槽都处于正常状态,然后再给出一个重分片计划,并询问我们的意见。在输入yes并按下Enter键之后,命令就会实施预定好的重分片计划:
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 5461 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5462 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5463 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5464 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5465 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5466 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5467 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5468 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5469 from 127.0.0.1:7001 to 127.0.0.1:7000:
Moving slot 5470 from 127.0.0.1:7001 to 127.0.0.1:7000:
通过执行info子命令,我们可以确认,之前指定的10个槽已经被迁移并指派给了节点7001负责:
$ ./redis-cli --cluster info 127.0.0.1:7001
127.0.0.1:7001 (e8dc4916...) -> 4 keys | 5452 slots | 1 slaves.
127.0.0.1:7000 (63000206...) -> 3 keys | 5471 slots | 1 slaves.
127.0.0.1:7002 (f5780366...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 8 keys in 3 masters.
0.00 keys per slot on average.
cluster选项的rebalance子命令允许用户在有需要时重新分配各个节点负责的槽数量,从而使得各个节点的负载压力趋于平衡:
rebalance <ip>:<port>
$ ./redis-cli --cluster rebalance 127.0.0.1:7001
>>> Performing Cluster Check (using node 127.0.0.1:7001)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 3 nodes. Total weight = 3.00
Moving 110 slots from 127.0.0.1:7000 to 127.0.0.1:7001
###############################################################################
在rebalance命令执行之后,3个节点的槽数量将趋于平均:可以使用./redis-cli --cluster info 127.0.0.1:7001
命令来查看
rebalance命令提供了很多可选项,它们可以让用户更精确地控制负载均衡操作的具体行为。比如,通过以下可选项,用户可以为不同的节点设置不同的权重,而权重较大的节点将被指派更多槽。这样一来,
用户就可以通过这个选项,让性能更强的节点负担更多负载:
--cluster-weight <node_id1>=<weight1> <node_id2>=<weight2> ...
在没有显式地指定权重的情况下,每个节点的默认权重为1.0。将一个节点的权重设置为0将导致它被撤销所有槽指派,成为一个空节点。
如果用户在执行负载均衡操作时,想要为尚未被指派槽的空节点也分配相应的槽,那么可以使用以下可选项:
--cluster-use-empty-masters
rebalance命令在执行时会根据各个节点目前负责的槽数量以及用户给定的权重计算出每个节点应该负责的槽数量(期望槽数量),如果这个槽数量与节点目前负责的槽数量之间的比率超过了指定的阈值,那
么就会触发槽的重分配操作。触发重分配操作的阈值默认为2.0,也就是期望槽数量与实际槽数量之间不能相差超过两倍,用户也可以通过以下可选项来指定自己想要的阈值:
--cluster-threshold <value>
除了上述可选项之外,用户还可以通过以下可选项来设置负载均衡操作是否使用流水线:
--cluster-pipeline <yes/no>
或者通过以下可选项设置负载均衡操作的最大可执行时限:
--cluster-timeout <time>
最后,rebalance命令在执行负载均衡操作的时候,通常会一个接一个地对节点的槽数量进行调整,但如果用户想要同时对多个节点实施调整,那么只需要给定以下可选项即可:
--cluster-simulate
如果rebalance命令在执行时发现集群并没有平衡的必要,那么它将不执行任何其他操作,直接退出:
$ redis-cli --cluster rebalance 127.0.0.1:30001
>>> Performing Cluster Check (using node 127.0.0.1:30001)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.00% threshold.
cluster选项的add-node子命令允许用户将给定的新节点添加到已有的集群当中,用户只需要依次给定新节点的地址以及集群中某个节点的地址即可:
add-node <new_host>:<port> <existing_host>:<port>
在默认情况下,add-node命令添加的新节点将作为主节点存在。如果用户想要添加的新节点为从节点,那么可以在执行命令的同时,通过给定以下两个可选项来将新节点设置为从节点:
--cluster-slave
--cluster-master-id <id>
其中可选项cluster-master-id的id参数用于设置从节点将要复制的主节点。
如何将节点30007添加到节点30001所在的集群当中:
$ redis-cli --cluster add-node 127.0.0.1:30007 127.0.0.1:30001
>>> Adding node 127.0.0.1:30007 to cluster 127.0.0.1:30001
>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: 4979f8583676c46039672fb7319e917e4b303707 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 4788fd4d92387fc5d38a2cd12f0c0d80fc0f6609 127.0.0.1:30004
slots: (0 slots) slave
replicates 4979f8583676c46039672fb7319e917e4b303707
S: b45a7f4355ea733a3177b89654c10f9c31092e92 127.0.0.1:30005
slots: (0 slots) slave
replicates 4ff303d96f5c7436ce8ce2fa6e306272e82cd454
S: 7c56ffba63e3758bc4c2e9b6a55caf294bb21650 127.0.0.1:30006
slots: (0 slots) slave
replicates 07e230805903e4e1657743a2e4d8811a59e2f32f
M: 4ff303d96f5c7436ce8ce2fa6e306272e82cd454 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 07e230805903e4e1657743a2e4d8811a59e2f32f 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 127.0.0.1:30007 to make it join the cluster.
[OK] New node added correctly.
当用户不再需要集群中的某个节点时,可以通过cluster选项的del-node子命令来移除该节点:
del-node <ip>:<port> <node_id>
其中命令的ip和port参数用于指定集群中的某个节点作为入口,而node_id则用于指定用户想要移除的节点的ID。从节点30001所在的集群中,移除ID为e1971eef02709cf4698a6fcb09935a910982ab3b的节点30007:
e1971eef02709cf4698a6fcb09935a910982ab3b的节点30007:
$ redis-cli --cluster del-node 127.0.0.1:30001 e1971eef02709cf4698a6fcb09935a910892ab3b
>>> Removing node e1971eef02709cf4698a6fcb09935a910982ab3b from cluster 127.0.0.1:30001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
通过cluster选项的call子命令,用户可以在整个集群的所有节点上执行给定的命令:
call host:port command arg arg .. arg
通过cluster选项的set-timeout子命令,用户可以为集群的所有节点重新设置cluster-node-timeout选项的值:
set-timeout <host>:<port> <milliseconds>
用户可以通过cluster选项的import子命令,将给定单机Redis服务器的数据导入集群中:
import <node-host>:<port> # 集群入口节点的IP地址和端口号
--cluster-from <server-host>:<port> # 单机服务器的IP地址和端口号
--cluster-copy # 使用复制导入
--cluster-replace # 覆盖同名键
在默认情况下,import命令在向集群导入数据的同时,还会删除单机服务器中的源数据。如果用户想要保留单机服务器中的数据,那么可以在执行命令的同时给定--cluster-copy选项。
此外,在导入数据的过程中,如果命令发现将要导入的键在集群数据库中已经存在(同名键冲突),那么命令在默认情况下将中断导入操作。如果用户想要使用导入的键去覆盖集群中已有的同名键,那么可以在执行命令的同时给定--cluster-replace选项。
$ redis-cli --cluster import 127.0.0.1:30001 --cluster-from 127.0.0.1:6379 --cluster-copy --
cluster-replace
>>> Importing data from 127.0.0.1:6379 to cluster 127.0.0.1:30001
>>> Performing Cluster Check (using node 127.0.0.1:30001)
M: 4979f8583676c46039672fb7319e917e4b303707 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 4788fd4d92387fc5d38a2cd12f0c0d80fc0f6609 127.0.0.1:30004
slots: (0 slots) slave
replicates 4979f8583676c46039672fb7319e917e4b303707
S: b45a7f4355ea733a3177b89654c10f9c31092e92 127.0.0.1:30005
slots: (0 slots) slave
replicates 4ff303d96f5c7436ce8ce2fa6e306272e82cd454
S: 7c56ffba63e3758bc4c2e9b6a55caf294bb21650 127.0.0.1:30006
slots: (0 slots) slave
replicates 07e230805903e4e1657743a2e4d8811a59e2f32f
M: 4ff303d96f5c7436ce8ce2fa6e306272e82cd454 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 07e230805903e4e1657743a2e4d8811a59e2f32f 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** Importing 3 keys from DB 0
Migrating alphabets to 127.0.0.1:30002: OK
Migrating number to 127.0.0.1:30002: OK
Migrating msg to 127.0.0.1:30002: OK
除了集群管理程序之外,Redis还提供了一簇以CLUSTER开头的集群命令,这些命令可以根据它们的作用分为集群管理命令和槽管理命令,前者管理的是集群及其节点,而后者管理的则是节点的槽分配情况。
需要注意的是,因为Redis的集群管理程序redis-cli--cluster实际上就是由CLUSTER命令实现的,所以这两者之间存在着千丝万缕的关系,某些redis-cli--cluster子命令甚至直接与某个CLUSTER子命令对应。
用户可以通过执行以下命令,将给定的节点添加至当前节点所在的集群中:
CLUSTER MEET ip port
CLUSTER MEET命令在向给定节点发送完握手信息之后将返回OK。
当用户执行CLUSTER MEET命令,尝试将一个给定的节点添加到当前节点所在的集群时,如果给定节点已经位于一个包含多个节点的集群当中,那么不仅给定节点会被添加到当前节点所在的集群,给定节点原集群内的其他节点也会自动合并到当前集群中。
127.0.0.1:7001> CLUSTER MEET 127.0.0.1 7004
OK
用户可以通过执行以下命令,查看集群内所有节点的相关信息:
CLUSTER NODES
CLUSTER NODES命令的结果通常由多个行组成,每个行都记录了一个节点的相关信息,行中的各项信息则由空格分隔。
当用户想要知道客户端正在连接的节点的运行ID时,可以执行以下命令:
CLUSTER MYID
因为不少集群命令都需要使用节点的运行ID作为参数,所以当我们需要对正在连接的节点执行某个使用运行ID作为参数的操作时,就可以使用CLUSTER MYID命令快速地获得节点的ID。
127.0.0.1:30001> CLUSTER MYID
"9cd23534bf654a47a2d4d8a4b2717c495ee31b40"
用户可以通过执行CLUSTER INFO命令,查看与集群以及当前节点有关的状态信息:
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:6 --集群包含6个节点
cluster_size:3 --集群中有3个节点被指派了槽
cluster_current_epoch:6 --集群当前所处的纪元为6
cluster_my_epoch:1 --节点当前所处的配置纪元为1
cluster_stats_messages_ping_sent:774301 --节点发送PING消息的数量
cluster_stats_messages_pong_sent:774642 --节点发送PONG消息的数量
cluster_stats_messages_sent:1548943 --节点目前总共发送了1548943条消息
cluster_stats_messages_ping_received:774637 --节点接收PING消息的数量
cluster_stats_messages_pong_received:774301 --节点接收PONG消息的数量
cluster_stats_messages_meet_received:5 --节点接收MEET消息的数量
cluster_stats_messages_received:1548943 --节点目前总共接收了1548943条消息
当用户不再需要集群中的某个节点时,可以通过执行以下命令将其移除:
CLUSTER FORGET node-id
这个命令接受节点的运行ID作为参数,并在成功执行之后返回OK作为结果。
与CLUSTER MEET命令引发的节点添加消息不一样,CLUSTER FORGET命令引发的节点移除消息并不会通过Gossip协议传播至集群中的其他节点:当用户向一个节点发送CLUSTER FORGET命令,让它去移除集群中的另一个节点时,接收到命令的节点只是暂时屏蔽了用户指定的节点,但这个被屏蔽的节点对于集群中的其他节点仍然是可见的。为此,要让集群真正地移除一个节点,用户必须向集群中的所有节点都发送相同的CLUSTER FORGET命令,并且这一动作必须在60s之内完成,否则被暂时屏蔽的节点就会因为Gossip协议的作用而被重新添加到集群中。
127.0.0.1:30001> CLUSTER FORGET 5f99406c27403564f34f4b5e39410714881ad98e -- 节点30005的运行ID
OK
127.0.0.1:30002> CLUSTER FORGET 5f99406c27403564f34f4b5e39410714881ad98e
OK
127.0.0.1:30003> CLUSTER FORGET 5f99406c27403564f34f4b5e39410714881ad98e
OK
127.0.0.1:30004> CLUSTER FORGET 5f99406c27403564f34f4b5e39410714881ad98e
OK
127.0.0.1:30006> CLUSTER FORGET 5f99406c27403564f34f4b5e39410714881ad98e
OK
CLUSTER REPLICATE命令接受一个主节点ID作为参数,并将执行该命令的节点变成给定主节点的从节点:
CLUSTER REPLICATE master-id
用户给定的主节点必须与当前节点位于相同的集群当中。此外,根据当前节点角色的不同,CLUSTER REPLICATE命令在执行时的情况也会有所不同:
在使用单机版本的Redis时,用户可以让一个从服务器去复制另一个从服务器,以此来构建一系列链式复制的服务器。
与这种做法不同,Redis集群只允许节点对主节点而不是从节点进行复制,如果用户尝试使用CLUSTER REPLICATE命令让一个节点去复制一个从节点,那么命令将返回一个错误:
127.0.0.1:7001> CLUSTER REPLICATE db3a54cfe722264bd91caef4d4af9701bf02223f --向命令传入一个从节点ID
(error) ERR I can only replicate a master, not a replica.
CLUSTER REPLICAS命令接受一个节点ID作为参数,然后返回该节点属下所有从节点的相关信息:
CLUSTER REPLICAS node-id
用户可以通过向从节点发送以下命令,让它发起一次对自身主节点的故障转移操作:
CLUSTER FAILOVER
因为接收到该命令的从节点会先将自身的数据库更新至与主节点完全一致,然后再执行后续的故障转移操作,所以这个过程不会丢失任何数据。
用户可以通过可选的FORCE选项和TAKEOVER选项来改变CLUSTERFAILOVER命令的行为:
CLUSTER FAILOVER [FORCE|TAKEOVER]
在给定了FORCE选项时,从节点将在不尝试与主节点进行握手的情况下,直接实施故障转移。这种做法可以让用户在主节点已经下线的情况下立即开始故障转移。
需要注意的是,即使用户给定了FORCE选项,从节点对主节点的故障转移操作仍然要经过集群中大多数主节点的同意才能够真正执行。但如果用户给定了TAKEOVER选项,那么从节点将在不询问集群中其他节点意见的情况下,直接对主节点实施故障转移。
用户可以通过在节点上执行CLUSTER RESET命令来重置该节点,以便在集群中复用该节点:
CLUSTER RESET [SOFT|HARD]
这个命令接受SOFT和HARD两个可选项作为参数,用于指定重置操作的具体行为(软重置和硬重置)。如果用户在执行CLUSTER RESET命令的时候没有显式地指定重置方式,那么命令默认将使用SOFT选项。
CLUSTER RESET命令在执行时,将对节点执行以下操作:
1) 遗忘该节点已知的其他所有节点。
2) 撤销指派给该节点的所有槽,并清空节点内部的槽-节点映射表。
3) 如果执行该命令的节点是一个从节点,那么将它转换成一个主节点。
4) 如果执行的是硬重置,那么为节点创建一个新的运行ID。
5) 如果执行的是硬重置,那么将节点的纪元和配置纪元都设置为0。
6) 通过集群节点配置文件的方式,将新的配置持久化到硬盘上。
需要注意的是,CLUSTER RESET命令只能在数据库为空的节点上执行,如果节点的数据库非空,那么命令将返回一个错误:
127.0.0.1:30002> CLUSTER RESET
(error) ERR CLUSTER RESET can't be called with master nodes containing keys
在正常情况下,CLUSTER RESET命令在正确执行之后将返回OK作为结果:
--清空数据库
127.0.0.1:30002> FLUSHALL
OK
--执行(软)重置
127.0.0.1:30002> CLUSTER RESET
OK
--节点在重置之后将遗忘之前发现过的所有节点
127.0.0.1:30002> CLUSTER NODES
309871e77eaccc0a4e260cf393547bf51ba11983 127.0.0.1:30002@40002 myself,master - 0 1542090339000 2
connected
--执行硬重置
127.0.0.1:30002> CLUSTER RESET HARD
OK
--节点在硬重置之后获得了新的运行ID
127.0.0.1:30002> CLUSTER NODES
b24d4a41c6a9c5633eb93caca15faed75398dd54 127.0.0.1:30002@40002 myself,master - 0 1542090339000 0
connected
用户可以通过执行以下命令,获知各个槽与集群节点之间的关联信息:
CLUSTER SLOTS
命令会返回一个嵌套数组,数组中的每个项记录了一个槽范围(slot range)及其处理者的相关信息,其中包括:
其中,每一项节点信息都由以下3项信息组成:
通过在节点上执行以下命令,我们可以将给定的一个或任意多个槽指派给当前节点进行处理:
CLUSTER ADDSLOTS slot [slot ...]
命令在成功执行指派操作之后将返回OK作为结果。
作为例子,以下代码演示了如何将尚未被指派的槽0~5指派给节点30001负责:
127.0.0.1:30001> CLUSTER ADDSLOTS 0 1 2 3 4 5
OK
我们可以通过执行CLUSTER SLOTS命令来确认这些槽已经被成功指派给了节点30001:
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 0
2) (integer) 5
3) 1) "127.0.0.1"
2) (integer) 30001
3) "9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f"
...
需要注意的是,CLUSTER ADDSLOTS只能对尚未被指派的槽执行指派操作,如果用户给定的槽已经被指派,那么命令将返回一个错误:
--尝试指派已被指派的槽,命令报错
127.0.0.1:30001> CLUSTER ADDSLOTS 0 1 2 3 4 5
(error) ERR Slot 0 is already busy
在使用CLUSTER ADDSLOTS命令将槽指派给节点负责之后,用户可以在有需要的情况下,通过执行以下命令撤销对节点的槽指派:
CLUSTER DELSLOTS slot [slot ...]
命令在执行成功之后将返回OK作为结果。
作为例子,假设现在有槽配置如下:
--槽0~5由节点30001负责
--槽6~5460未指派
--槽5461~10922由节点30002负责
--槽10923~16383由节点30003负责
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 0
2) (integer) 5
3) 1) "127.0.0.1"
2) (integer) 30001
3) "9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f"
2) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 30003
3) "a80b64eedcd15329bc0dc7b71652ecddccf6afe8"
3) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 30002
3) "b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a"
那么我们可以通过对节点30001执行以下命令,撤销对该节点的槽0~5的指派:
127.0.0.1:30001> CLUSTER DELSLOTS 0 1 2 3 4 5
OK
通过再次执行CLUSTER SLOTS命令,我们可以确认槽0~5已经不再由节点30001负责,并且已经重新回到未指派状态:
--只有槽5461~16383被指派了,其他槽都处于未指派状态
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 30003
3) "a80b64eedcd15329bc0dc7b71652ecddccf6afe8"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 30002
3) "b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a"
需要注意的是,在执行CLUSTER DELSLOTS命令时,用户给定的必须是已经指派给当前节点的槽,尝试撤销一个未指派的槽将引发一个错误:
127.0.0.1:30001> CLUSTER DELSLOTS 0 1 2 3 4 5
(error) ERR Slot 0 is already unassigned
通过在一个节点上执行以下命令,我们可以撤销对该节点的所有槽指派,让它不再负责处理任何槽:
CLUSTER FLUSHSLOTS
CLUSTER FLUSHSLOTS命令在执行成功之后将返回OK作为结果,执行这个命令相当于对该节点负责的所有槽执行CLUSTER DELSLOTS命令。
举个例子,对于具有以下槽配置的集群来说:
--节点30001负责槽0~5460 ,节点30002负责槽5461~10922 ,节点30003负责槽10923~16383
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 0
2) (integer) 5460
3) 1) "127.0.0.1"
2) (integer) 30001
3) "9e2ee45f2a78b0d5ab65cbc0c97d40262b47159f"
2) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 30003
3) "a80b64eedcd15329bc0dc7b71652ecddccf6afe8"
3) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 30002
3) "b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a"
如果我们对节点30001执行以下命令,那么该节点对于槽0~5460的指派将会被撤销:
127.0.0.1:30001> CLUSTER FLUSHSLOTS
OK
再次执行CLUSTER SLOTS命令可以看到,集群现在只有两个节点被指派了槽,而节点30001并不在此列:
--槽0~5460尚未被指派,节点30002负责槽5461~10922 ,节点30003负责槽10923~16383
127.0.0.1:30001> CLUSTER SLOTS
1) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 30003
3) "a80b64eedcd15329bc0dc7b71652ecddccf6afe8"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 30002
3) "b2c7a5ca5fa6de72ac2842a2196ab2f4a5c82a6a"
需要注意的是,用户在执行CLUSTER FLUSHSLOTS命令之前,必须确保节点的数据库为空,否则节点将拒绝执行命令并返回一个错误:
--尝试对非空节点30002执行CLUSTER FLUSHSLOTS
127.0.0.1:30002> DBSIZE
(integer) 3
127.0.0.1:30002> CLUSTER FLUSHSLOTS
(error) ERR DB must be empty to perform CLUSTER FLUSHSLOTS.
通过对给定键执行以下命令,我们可以知道该键所属的槽:
CLUSTER KEYSLOT key
比如,如果我们想要知道message键以及counter::12345键属于哪个槽,那么可以执行以下命令:
127.0.0.1:30001> CLUSTER KEYSLOT message
(integer) 11537 -- message键属于槽11537
127.0.0.1:30001> CLUSTER KEYSLOT counter::12345
(integer) 12075 -- counter::12345键属于槽12075
最后,正如前文所说,带有相同散列标签的键将被分配到相同的槽中:
--两个带有相同散列标签{user}的键
127.0.0.1:30001> CLUSTER KEYSLOT {user}::256
(integer) 5474
127.0.0.1:30001> CLUSTER KEYSLOT {user}::10086
(integer) 5474
通过执行以下命令,用户可以查看给定槽包含的键数量:
CLUSTER COUNTKEYSINSLOT slot
举个例子,假如我们想要知道槽523包含了多少个键,那么只需要执行以下命令即可:
127.0.0.1:30001> CLUSTER COUNTKEYSINSLOT 523
(integer) 2
用户在使用CLUSTER COUNTKEYSINSLOT命令时需要特别注意一点,即CLUSTER COUNTKEYSINSLOT命令只会在执行该命令的节点中进行计数:
如果执行命令的节点并不是负责处理给定槽的节点,那么命令将找不到任何属于给定槽的键。在这种情况下,命令只会单纯地返回0作为执行结果。
用户可以通过执行以下命令,获取指定槽包含的键:
CLUSTER GETKEYSINSLOT slot count
命令的slot参数用于指定槽,而count参数则用于指定命令允许返回的最大键数量。
与CLUSTER COUNTKEYSINSLOT命令一样,CLUSTER GETKEYSINSLOT命令在尝试获取槽包含的键时也只会在执行该命令的节点中进行查找:如果执行命令的节点并不是负责处理给定槽的节点,那么命令将无法找到任何可以返回的键。因此,为了正确地获取槽包含的键,用户必须向正确的节点发送CLUSTER GETKEYSINSLOT命令。
CLUSTER SETSLOT命令拥有4个子命令,它们可以改变给定槽在节点中的状态,从而实现节点之间的槽迁移以及集群重分片:
CLUSTER SETSLOT slot IMPORTING source-node-id
CLUSTER SETSLOT slot MIGRATING destination-node-id
CLUSTER SETSLOT slot NODE node-id
CLUSTER SETSLOT slot STABLE
接下来将分别介绍这4个子命令。
通过在节点上执行IMPORTING子命令,用户可以让节点的指定槽进入 “导入中”(importing)状态,处于该状态的槽允许从源节点中导入槽数据:
CLUSTER SETSLOT slot IMPORTING source-node-id
该命令在成功执行之后将返回OK作为结果。
通过在节点上执行MIGRATING子命令,用户可以让节点的指定槽进入“迁移中”(migrating)状态,处于该状态的槽允许向目标节点转移槽数据:
CLUSTER SETSLOT slot MIGRATING destination-node-id
在将槽数据从源节点迁移至目标节点之后,用户可以在集群的任一节点执行以下命令,正式将槽指派给目标节点负责:
CLUSTER SETSLOT slot NODE node-id
集群的其中一个节点在执行了NODE子命令之后,对给定槽的新指派信息将被传播至整个集群,目标节点在接收到这一信息之后将移除给定槽的“导入中”状态,而源节点在接收到这一信息之后将移除给定槽的“迁移中”状态。
通过执行以下命令,用户可以清除节点指定槽的“导入中”或“迁移中”状态:
CLUSTER SETSLOT slot STABLE
该命令在成功执行之后将返回OK作为结果:
127.0.0.1:30001> CLUSTER SETSLOT 5460 STABLE
OK
正如之前所说,因为槽在成功迁移之后会由于NODE子命令的作用而自动移除相应节点的“导入中”和“迁移中”状态,所以在正常情况下,用户并不需要执行STABLE子命令。STABLE子命令的唯一作用,就是在槽迁移出错或者重分片出错时,手动移除相应节点的槽状态。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。