Redis Cluster
在学习Redis Cluster之前,咱们先理解为什么须要集群,当遇到单机内存、并发、流量等瓶颈时,单机曾经无奈满足咱们的要求的时候,能够采纳Cluster架构计划达到负载平衡的目标。
数据分区概论
分布式数据库首先要解决把整个数据集依照分区规定映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。
常见的分区规定有哈希分区和程序分区两种。
首先看一下比照
散布形式 | 特点 | 典型产品 |
---|---|---|
哈希分区 | 数据分散度高、键值散布无业务无关、无奈程序拜访、反对批量操作。 | 一致性哈希: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节点,如下图所示
这种形式相比节点取余最大的益处在于退出和删除节点只影响哈希环中相邻的节点,对其余节点无影响。
但一致性哈希也存在一些问题:
- 加减节点会造成哈希环中局部数据无奈命中(例如一个key增减节点前映射到第n2个节点,因而它的数据是保留在第n2个节点上的;当咱们减少一个节点后被映射到n5节点上了,此时咱们去n5节点下来找这个key对应的值是找不到的,见下图),须要手动解决或者疏忽这部分数据,因而一致性哈希罕用于缓存场景。
- 当应用大量节点时,节点变动将大范畴影响哈希环中数据映射,因而这种形式不适宜大量数据节点的分布式计划。
- 一般的一致性哈希分区在增减节点时须要增加一倍或减去一半节点能力保证数据和负载的平衡。
虚构槽分区
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命令是一个异步命令,执行之后立即返回。外部发动与指标节点进行握手通信,如下图所示:
- 节点7000本地创立7001节点信息对象,并发送meet音讯。
- 节点7001承受到meet音讯后,保留7000节点信息并回复pong音讯。
- 之后节点7000和7001彼此定期通过ping/pong音讯进行失常的节点通信。
这个时候咱们再执行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还提供了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
槽迁徙打算
下面咱们增加了两个新节点:7006、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迁徙。 - 反复执行步骤3和步骤4,直到槽下所有的键值数据迁徙到指标节点。
- 向集群内所有主节点发送:
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进行迁徙
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、7001、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
ASK重定向
Redis集群反对在线迁徙槽(slot)和数据来实现程度伸缩,当slot对应的数据从源节点到指标节点迁徙过程中,客户端须要做到智能辨认,保障键命令可失常执行。例如当一个slot数据从源节点迁徙到指标节点时,期间可能呈现一部分数据在源节点,而另一部分在指标节点。
当呈现上述情况时,客户端键命令执行流程将发生变化,如下所示:
- 客户端依据本地slots缓存发送命令到源节点,如果存在键对象则直 接执行并返回后果给客户端。
- 如果键对象不存在,则可能存在于指标节点,这时源节点会回复 ASK重定向异样。格局如下:
(error) ASK {slot} {targetIP}:{targetPort}
。 - 客户端从ASK重定向异样提取出指标节点信息,发送
asking
命令到指标节点关上客户端连贯标识,再执行键命令。如果存在则执行,不存在则返回不存在信息。
ASK和MOVED区别
- ASK重定向阐明集群正在进行slot数据迁徙,客户端无奈晓得什么时候迁徙实现,因而只能是临时性的重定向,客户端不会更新slots缓存。
- 然而MOVED重定向阐明键对应的槽曾经明确指定到新的节点,因而须要更新slots缓存。
故障发现
- 当集群内某个节点呈现问题时,须要通过一种强壮的形式保障辨认出节点是否产生了故障。Redis集群内节点通过ping/pong音讯实现节点通信,音讯岂但能够流传节点槽信息,还能够流传其余状态如:主从状态、节点故障等。
-
因而故障发现也是通过音讯流传机制实现的,次要环节包含:
- 主观下线 (pfail):指某个节点认为另一个节点不可用,即下线状态,这个状态并不是最终的故障断定,只能代表一个节点的意见,可能存在误判状况。
- 主观下线(fail):指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的后果。如果是持有槽的主节点故障,须要为该节点进行故障转移。
主观下线
集群中每个节点都会定期向其余节点发送ping音讯,接管节点回复pong音讯作为响应。如果在cluster-node-timeout工夫内通信始终失败,则发送节点会认为接管节点存在故障,把接管节点标记为主观下线(pfail)状态。
主观下线流程:
- 节点a发送ping音讯给节点b,如果通信失常将接管到pong音讯,节点a更新最近一次与节点b的通信工夫。
- 如果节点a与节点b通信呈现问题则断开连接,下次会进行重连。如果始终通信失败,则节点a记录的与节点b最初通信工夫将无奈更新。
- 节点a内的定时工作检测到与节点b最初通信工夫超过
cluster-nodetimeout
时,更新本地对节点b的状态为主观下线(pfail)。
主观下线
当半数以上持有槽的主节点都标记某节点主观下线。
主观下线流程:
- 当音讯体内含有其余节点的pfail状态会判断发送节点的状态,如果发送节点是主节点则对报告的pfail状态解决,从节点则疏忽。
- 找到pfail对应的节点构造,更新clusterNode外部下线报告链表。
- 依据更新后的下线报告链表告尝试进行主观下线。
尝试主观下线
- 首先统计无效的下线报告数量,如果小于集群内持有槽的主节点总数的一半则退出。
- 当下线报告大于槽主节点数量一半时,标记对应故障节点为主观下线状态。
- 向集群播送一条fail音讯,告诉所有的节点将故障节点标记为主观下线,fail音讯的音讯体只蕴含故障节点的ID。
故障复原
故障节点变为主观下线后,如果下线节点是持有槽的主节点则须要在它的从节点中选出一个替换它,从而保障集群的高可用。下线主节点的所有从节点承当故障复原的任务,当从节点通过外部定时工作发现本身复制的主节点进入主观下线时,将会触发故障复原流程。
查看资格
- 每个从节点都要查看最初与主节点断线工夫,判断是否有资格替换故障的主节点。
- 如果从节点与主节点断线工夫超过
cluster-node-timeout * cluster-slave-validity-factor
,则以后从节点不具备故障转移资格。参数cluster-slavevalidity-factor
用于从节点的无效因子,默认为10。
筹备选举工夫
当从节点合乎故障转移资格后,更新触发故障选举的工夫,只有达到该工夫后能力执行后续流程。
主节点b进入主观下线后,它的三个从节点依据本身复制偏移量设置提早选举工夫,如复制偏移量最大的节点slave b-1提早1秒执行,保障复制提早低的从节点优先发动选举。
选举投票
- 只有持有槽的主节点才会解决故障选举音讯。
- 投票过程其实是一个领导者选举的过程,如集群内有N个持有槽的主节点代表有N张选票。因为在每个配置纪元内持有槽的主节点只能投票给一个从节点,因而只能有一个从节点取得N/2+1的选票,保障可能找出惟一的从节点。
- Redis集群没有间接应用从节点进行领导者选举,次要因为从节点数必须大于等于3个能力保障凑够N/2+1个节点,将导致从节点资源节约。应用集群内所有持有槽的主节点进行领导者选举,即便只有一个从节点也能够实现选举过程。
- 当从节点收集到N/2+1个持有槽的主节点投票时,从节点能够执行替换主节点操作,例如集群内有5个持有槽的主节点,主节点b故障后还有4个, 当其中一个从节点收集到3张投票时代表取得了足够的选票能够进行替换主节点操作。
替换主节点
当从节点收集到足够的选票之后,触发替换主节点操作:
- 以后从节点勾销复制变为主节点。
- 执行
clusterDelSlot
操作撤销故障主节点负责的槽,并执行clusterAddSlot
把这些槽委派给本人。 - 向集群播送本人的pong音讯,表明曾经替换了故障从节点。
发表回复