在学习Redis Cluster以前,咱们先了解为何须要集群,当遇到单机内存、并发、流量等瓶颈时,单机已经没法知足我让节点7000和7001等节点进们的要求的时候,能够采用Cluster架构方案达到负载均衡的目的。node
分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每一个节点负责总体数据的一个子集。redis
常见的分区规则有哈希分区和顺序分区两种。shell
首先看一下对比数据库
分布方式 | 特色 | 典型产品 |
---|---|---|
哈希分区 | 数据分散度高、键值分布无业务无关、没法顺序访问、支持批量操做。 | 一致性哈希:Mecache、Redis Cluster ... |
顺序分区 | 数据分散度易倾斜、键值业务相关、能够顺序访问、支持批量操做。 | BigTable、HBase |
好比:1-100个数字,要保存到3个节点上,每一个节点平均存储,1-33存储在第1个节点,34-66存储到2节点,剩余存储到3节点。缓存
顺序存储经常使用在关系型存储上。架构
由于Redis Cluster采用的哈希分区,因此咱们看一下常见的哈希分区有哪几种。并发
好比100个数据,对每一个数据进行hash运算以后,再于节点数进行取余运算,根据余数保存在不一样节点上。负载均衡
缺点就是:当节点数量变化时,如扩容或收缩节点,数据节点映射关系须要从新计算,会致使数据的从新迁移。运维
为系统中每一个节点分配一个token,范围通常在0~2的32次方,这些token构成一个哈希环。数据读写执行节点查找操做时,先根据key计算hash值,而后顺时针找到第一个大于等于该哈希值的token节点,以下图所示异步
这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其余节点无影响。
但一致性哈希也存在一些问题:
Redis Cluster采用的就是虚拟槽分区。槽的范围是0~16383,将16384个槽平均分配给节点,由节点进行管理。
每次将key进行hash运算,对16383进行取余,而后去redis对应的槽进行查找。
槽是集群内数据管理和迁移的基本单位。采用大范围槽的主要目的是为了方便数据拆分和集群扩展。每一个节点会负责必定数量的槽。
好比咱们如今有5个集群,每一个节点平均大约负责3276个槽。Redis Cluster 计算公式:slot=CRC16(key)&16383。每个节点负责维护一部分槽以及槽所映射的键值数据。
Redis虚拟槽分区的特色:
Redis集群通常由多个节点组成,节点数量至少为6个才能保证组成完整高可用的集群。每一个节点须要开启配置cluster-enabled yes
,让Redis运行在集群模式下。
首先咱们在redis文件中建立三个文件夹:config
、data
、log
。分别存放配置、数据和日志相关文件。
配置相关redis.conf
#节点端口 port ${port} # 守护进程模式启动(可选) daemonize yes # 开启集群模式 cluster-enabled yes # 节点超时时间,单位毫秒 cluster-node-timeout 15000 # 集群内部配置文件 cluster-config-file /usr/local/redis/config/nodes-${port}.conf # 节点宕机后是否整个集群不可用 cluster-require-full-coverage no dir /usr/local/redis/data/ dbfilename dump-${port}.rdb logfile ${port}.log # 其他的配置与redis.conf默认配置文件一致便可
6个节点所有配完成后就能够开启了。
[root@localhost config]# ls redis-7000.conf redis-7001.conf redis-7002.conf redis-7003.conf redis-7004.conf redis-7005.conf
[root@localhost redis]# redis-server config/redis-7000.conf [root@localhost redis]# cd config [root@localhost config]# cat nodes-7000.conf f4deba14aac6494e95e3e4ad060c94b8c82df7ec :0 myself,master - 0 0 0 connected vars currentEpoch 0 lastVoteEpoch 0 [root@localhost config]# cd .. [root@localhost redis]# redis-server config/redis-7001.conf [root@localhost redis]# redis-server config/redis-7002.conf [root@localhost redis]# redis-server config/redis-7003.conf [root@localhost redis]# redis-server config/redis-7004.conf [root@localhost redis]# redis-server config/redis-7005.conf [root@localhost redis]# cd config [root@localhost config]# ll 总用量 288 -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7000.conf -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7001.conf -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7002.conf -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7003.conf -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7004.conf -rw-r--r--. 1 root root 112 12月 17 04:00 nodes-7005.conf -rw-r--r--. 1 root root 41650 12月 17 03:59 redis-7000.conf -rw-r--r--. 1 root root 41649 12月 17 03:59 redis-7001.conf -rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7002.conf -rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7003.conf -rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7004.conf -rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7005.conf [root@localhost config]# cat nodes-7005.conf d1e8e8e42be8d3b2f3f44d197138e54d91170442 :0 myself,master - 0 0 0 connected vars currentEpoch 0 lastVoteEpoch 0 [root@localhost config]#
检查节点日志是否正确:
sudo cat /usr/local/redis/conf/nodes-${port}.conf
文件内容记录了集群初始状态,这里最重要的是节点ID,它是一个40位16进制字符串,用于惟一标识集群内一个节点,以后不少集群操做都要借助于节点ID来完成。须要注意是,节点ID不一样于运行ID:节点ID在集群初始化 时只建立一次,节点重启时会加载集群配置文件进行重用,而Redis的运行ID每次重启都会变化。
咱们如今启动6个节点,但每一个节点彼此并不知道对方的存在,下面经过节点握手让6个节点彼此创建联系从而组成一个集群。
[root@localhost redis]# ps -ef |grep redis root 1388 1 0 09:10 ? 00:00:00 redis-server *:7000 [cluster] root 1392 1 0 09:10 ? 00:00:00 redis-server *:7001 [cluster] root 1396 1 0 09:10 ? 00:00:00 redis-server *:7002 [cluster] root 1400 1 0 09:10 ? 00:00:00 redis-server *:7003 [cluster] root 1404 1 0 09:10 ? 00:00:00 redis-server *:7004 [cluster] root 1408 1 0 09:10 ? 00:00:00 redis-server *:7005 [cluster]
节点握手是指一批运行在集群模式下的节点经过Gossip协议彼此通讯, 达到感知对方的过程
节点握手是集群彼此通讯的第一步,由客户端发起下面的命令:
cluster meet {ip} {port}
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7002 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7003 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7004 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7005 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7006 OK
上面执行命令以后让节点7000和7001等节点进行握手通讯。cluster meet
命令是一个异步命令,执行以后马上返回。内部发起与目标节点进行握手通讯。
这个时候咱们再执行cluster nodes
能够看到已经检测到其它节点了。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 master - 0 1609463858135 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609463860149 1 connected f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 master - 0 1609463857127 3 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 master - 0 1609463859143 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609463861156 2 connected
节点创建握手以后集群还不能正常工做,这时集群处于下线状态,全部的数据读写都被禁止。经过以下命令能够看到:
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 127.0.0.1:7000> set jack hello (error) CLUSTERDOWN The cluster is down
经过cluster info
命令能够获取集群当前状态:
127.0.0.1:7000> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:0 cluster_current_epoch:5 cluster_my_epoch:0 cluster_stats_messages_sent:670 cluster_stats_messages_received:521
能够看到咱们如今的状态是fail
,被分配的槽 cluster_slots_assigned
是0,因为目前全部的槽没有分配到节点,所以集群没法完成槽到节点的映射。只有当16384个槽所有分配给节点后,集群才进入在线状态。
Redis集群把全部的数据映射到16384个槽中。每一个key会映射为一个固定的槽,只有当节点分配了槽,才能响应和这些槽关联的键命令。经过cluster addslots
命令为节点分配槽。由于咱们有6个节点,咱们是三主三从的模式,因此只用给三个主节点进行配置便可。
redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0..5461} redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5462..10922} redis-cli -h 127.0.0.1 -p 7002 cluster addslots {10923..16383}
配置成功后,咱们再进入节点看一下:
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 127.0.0.1:7000> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:5 cluster_my_epoch:0 cluster_stats_messages_sent:1384 cluster_stats_messages_received:1235
能够看到,cluster_state
和 cluster_slots_assigned
都没有问题。
目前还有三个节点没有使用,做为一个完整的集群,每一个负责处理槽的节点应该具备从节点,保证当它出现故障时能够自动进行故障转移。
集群模式下,Reids节点角色分为主节点和从节点。首次启动的节点和被分配槽的节点都是主节点,从节点负责复制主节点槽信息和相关的数据。使用cluster replicate {node-id}
命令让一个节点成为从节点。其中命令执行必须在对应的从节点上执行,node-id是要复制主节点的节点ID。
咱们首先找到三个已经配置槽的节点的node-id。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 master - 0 1609464545892 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609464547906 1 connected 5462-10922 f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461 d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 master - 0 1609464546899 3 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 master - 0 1609464549923 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609464548916 2 connected 10923-16383
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7003 cluster replicate f4deba14aac6494e95e3e4ad060c94b8c82df7ec OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7004 cluster replicate 9a8abb84bcc8301a8f11c664471159dc0bf23a62 OK [root@localhost redis]# redis-cli -h 127.0.0.1 -p 7005 cluster replicate d438b4689776cb6cd6b6d0eaecb7576669c7b3fe OK
完成后咱们查看是否已经ok。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609464847442 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609464846435 1 connected 5462-10922 f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461 d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609464849456 3 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609464848449 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609464850468 2 connected 10923-16383
目前为止,咱们依照Redis协议手动创建一个集群。它由6个节点构成, 3个主节点负责处理槽和相关数据,3个从节点负责故障转移。
咱们以前分别使用命令搭建了一个完整的集群,可是命令过多,当集群节点众多时,必然会加大搭建集群的复杂度和运维成本。所以redis还提供了redis-cli --cluster来搭建集群。
首先咱们仍是启动六个单独的节点。
使用下面命令进行安装,--cluster-replicas 1
指定集群中每一个主节点配备几个从节点,这里设置为1。而且该命令会本身建立主节点和分配从节点,其中前3个是主节点,后3个是从节点,后3个从节点分别复制前3个主节点。
redis-cli --cluster create --cluster-replicas 1 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
最后的输出报告说明:16384个槽所有被分配,集群建立成功。这里须要注意命令中节点的地址必须是不包含任何槽/数据的节点,不然会拒绝建立集群。
若是不想要从节点则不填写该参数便可--cluster-replicas 1
。
最后咱们可使用下面命令进行查看是否已经ok。
redis-cli --cluster check 127.0.0.1:7000
Redis集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的状况下,能够为集群添加节点进行扩容也能够下线部分节点进行缩容。原理可抽象为槽和对应数据在不一样节点之间灵活移动。
当咱们如今有三个节点,此时想增长6385节点,也就是每一个节点把一部分槽和数据迁移到新的节点6385,每一个节点负责的槽和数据相比以前变少了从而达到了集群扩容的目的。
以前咱们有6个节点,7000~7005节点。
如今咱们增长两个单独的节点也就是7006和7007。而后7006节点当作主节点,7007当作从节点。新节点跟集群内的节点配置保持一致,便于管理统一。
随后咱们进行启动
[root@localhost redis]# redis-server config/redis-7006.conf [root@localhost redis]# redis-server config/redis-7007.conf
这个时候咱们的两个新的节点只是单独运行,并无加入集群中。能够看到下面并无7006和7007节点。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609467765084 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609467769137 1 connected 5462-10922 f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461 d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609467767119 3 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609467768127 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609467766110 2 connected 10923-16383
结构图以下:
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7006 redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7007
集群内新旧节点通过一段时间的ping/pong消息通讯以后,全部节点会发现新节点并将它们的状态保存到本地。
随后咱们再进行查看cluster nodes
。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609468208783 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609468204768 1 connected 5462-10922 f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461 d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609468210798 3 connected 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 127.0.0.1:7006 master - 0 1609468209790 6 connected 55b028fbd0a0207b6acc6e2b1067bf79f3090534 127.0.0.1:7007 master - 0 1609468206777 7 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609468205773 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609468206274 2 connected 10923-16383
而后咱们把7007设置为7006的从节点
redis-cli -h 127.0.0.1 -p 7007 cluster replicate 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb
再次查看已经OK。
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609470748800 4 connected 9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609470750824 1 connected 5462-10922 f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461 d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609470745778 3 connected 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 127.0.0.1:7006 master - 0 1609470746785 6 connected 55b028fbd0a0207b6acc6e2b1067bf79f3090534 127.0.0.1:7007 slave 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 0 1609470751833 7 connected 7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609470749817 5 connected d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609470747795 2 connected 10923-16383
上面咱们添加了两个新节点:700六、7007。其中7006做为主节点存储数据,7007做为从节点复制7006。下面咱们要把其余节点的槽和数据迁移到7006这个节点中。
再迁移后原有节点负责的槽数量变为4096个。
数据迁移过程是逐个槽进行的。流程以下:
cluster setslot {slot} importing {sourceNodeId}
命令,让目标节点准备导入槽数据。cluster setslot {slot} migrating {targetNodeId}
命令,让源节点准备迁出槽数据。cluster getkeysinslot {slot} {count}
命令,每次获取count个属于槽的键。migrate {targetIP} {targetPort} key 0 {timeout}
命令,把指定的key迁移。cluster setslot {slot} node {targetNodeId}
命令,通知槽分配给目标节点。伪代码以下:
def move_slot(source,target,slot): # 目标节点准备导入槽 target.cluster("setslot",slot,"importing",source.nodeId); # 源节点准备全出槽 source.cluster("setslot",slot,"migrating",target.nodeId); while true : # 批量从源节点获取键 keys = source.cluster("getkeysinslot",slot,pipeline_size); if keys.length == 0: # 键列表为空时,退出循环 break; # 批量迁移键到目标节点 source.call("migrate",target.host,target.port,"",0,timeout,"keys",keys); # 向集群全部主节点通知槽被分配给目标节点 for node in nodes: if node.flag == "slave": continue; node.cluster("setslot",slot,"node",target.nodeId);
redis-cli --cluster reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout <arg> --pipeline <arg>
host
:port:必传参数,集群内任意节点地址,用来获取整个集群信息。--from
:制定源节点的id,若是有多个源节点,使用逗号分隔,若是是all源节点变为集群内全部主节点,在迁移过程当中提示用户输入。--to
:须要迁移的目标节点的id,目标节点只能填写一个,在迁移过程 中提示用户输入。--slots
:须要迁移槽的总数量,在迁移过程当中提示用户输入。--yes
:当打印出reshard执行计划时,是否须要用户输入yes确认后再执行reshard。--timeout
:控制每次migrate操做的超时时间,默认为60000毫秒。·--pipeline
:控制每次批量迁移键的数量,默认为10。开始迁移:
redis-cli --cluster reshard 127.0.0.1:7000
输入须要迁移的槽数量,此处咱们输入4096。
目标节点ID,只能指定一个,由于咱们须要迁移到7006中,所以下面输入7006的ID。
以后输入源节点的ID,redis会从这些源节点中平均取出对应数量的槽,而后迁移到6385中,下面咱们分别输入7000、700一、7002的节点ID。最后要输入done
表示结束。
最后输入yes便可。
咱们能够检查一下节点之间的平衡性
redis-cli --cluster rebalance 127.0.0.1:6380
全部主节点负责的槽数量差别在2%之内,就算集群节点数据相对均匀,无需调整。
收缩正好和扩容迁移方向相反,7006变为源节点,其余主节点变为目标节点,源节点须要把自身负责的4096个槽均匀地迁移到其余主节点上。
具体步骤和上述扩容相似,这里就不演示。
在集群模式下,Redis接收任何键相关命令时首先计算键对应的槽,再根据槽找出所对应的节点,若是节点是自身,则处理键命令;不然回复MOVED重定向错误,通知客户端请求正确的节点。
由于咱们执行cluster keyslot hello
以后,发现槽的位置在866,在咱们之中,因此直接返回。
127.0.0.1:7000> set hello world OK 127.0.0.1:7000> cluster keyslot hello (integer) 866 127.0.0.1:7000> get hello "world"
因为键对应槽是6918,不属于7000节点,则回复MOVED {slot} {ip} {port}格式重定向信息:
127.0.0.1:7000> set test hello (error) MOVED 6918 127.0.0.1:7001
咱们能够切换到7001发送命令便可成功。
127.0.0.1:7001> set test hello OK
用redis-cli命令时,能够加入-c参数支持自动重定向,简化手动发起重定向操做。
[root@localhost config]# redis-cli -h 127.0.0.1 -p 7000 -c 127.0.0.1:7000> set test hello -> Redirected to slot [6918] located at 127.0.0.1:7001 OK
Redis集群支持在线迁移槽(slot)和数据来完成水平伸缩,当slot对应的数据从源节点到目标节点迁移过程当中,客户端须要作到智能识别,保证键命令可正常执行。例如当一个slot数据从源节点迁移到目标节点时,期间可能出现一部分数据在源节点,而另外一部分在目标节点。
当出现上述状况时,客户端键命令执行流程将发生变化,以下所示:
(error) ASK {slot} {targetIP}:{targetPort}
。asking
命令到目标节点打开客户端链接标识,再执行键命令。若是存在则执行,不存在则返回不存在信息。所以故障发现也是经过消息传播机制实现的,主要环节包括:
集群中每一个节点都会按期向其余节点发送ping消息,接收节点回复pong消息做为响应。若是在cluster-node-timeout时间内通讯一直失败,则发送节点会认为接收节点存在故障,把接收节点标记为主观下线(pfail)状态。
主观下线流程:
cluster-nodetimeout
时,更新本地对节点b的状态为主观下线(pfail)。当半数以上持有槽的主节点都标记某节点主观下线。
客观下线流程:
故障节点变为客观下线后,若是下线节点是持有槽的主节点则须要在它的从节点中选出一个替换它,从而保证集群的高可用。下线主节点的全部从节点承担故障恢复的义务,当从节点经过内部定时任务发现自身复制的主节点进入客观下线时,将会触发故障恢复流程。
cluster-node-timeout * cluster-slave-validity-factor
,则当前从节点不具有故障转移资格。参数cluster-slavevalidity-factor
用于从节点的有效因子,默认为10。当从节点符合故障转移资格后,更新触发故障选举的时间,只有到达该时间后才能执行后续流程。
主节点b进入客观下线后,它的三个从节点根据自身复制偏移量设置延迟选举时间,如复制偏移量最大的节点slave b-1延迟1秒执行,保证复制延迟低的从节点优先发起选举。
当从节点收集到足够的选票以后,触发替换主节点操做:
clusterDelSlot
操做撤销故障主节点负责的槽,并执行clusterAddSlot
把这些槽委派给本身。