此时可以连接任意一个端口的redis
,比如6371
:
redis-cli -p 6371
此时不论连接上任意一个节点,都可以视作连接上了整个集群。
cluster nodes
执行以上命令,可以看到当前节点集群内的所有节点。
重定向
尝试插入数据:
报错了,因为当前集群发生了分片,每个分片都只能存储一部分数据,key1
经过哈希运算,发现并不是这个节点可以存储的值,于是就报错了。
想要解决这个问题,可以在启动时加上-c
选项,此时插入数据,会自动重定向到对应的节点。
redis-cli -c -p 6371
如图,每次操作数据是,如果该数据不属于当前分片,就会触发一次重定向,自动跳转到对应的客户端。命令行前面的端口号一直在改变,这就说明我们的客户端一直在切换。
但是redis
中,有一些命令同时操作多个key
,比如最后一个命令mget
,此时又报错了。因为这几个key
属于不同分片,那么就无法同时处理,因此在集群的情况下,最好不要一次性操作多个key
。
故障转移
如果在集群中,某一个分片的主节点宕机了,会发生什么?在部署集群时,并没有引入哨兵节点,但是集群也会完成哨兵的工作,如果主节点宕机了,集群会自动完成重新选主的过程。
如图:
首先通过docker stop redis1
,关掉了redis1
节点,也就是xxx.101
下线了,而这是一个主节点。登录6372
端口的客户端,查看当前集群,可以发现xxx.106
成为了新的主节点,而xxx.106
原先是xxx.101
的从节点。
重启redis1
,其变为了reids6
的从节点。
此处集群的故障转移,和哨兵的故障转移是有一些差别的,接下来就讲解集群中是如何完成故障转移的。
- 故障判定
集群中的所有节点,都会周期性的使用心跳包进行通信
- 节点A 给 节点B 发送
ping
包,B 就会给 A 返回一个pong
包,ping
和pong
除了消息类型属性之外,其他部分都是一样的,这里包含了集群的配置信息:- 节点的id
- 该节点从属于哪个分片
- 是主节点还是从节点
- 从属于谁
- 持有哪些哈希槽的位图
- 每个节点,每秒钟都会给一些随机的节点发起
ping
包,而不是全发一遍,这样设定是为了避免在节点很多的时候,心跳包也非常多 - 当 节点A 给 节点B 发起
ping
包,B不能如期回应的时候,此时 A 就会尝试重置和 B 的 tcp 连接,看能否连接成功,如果仍然连接失败,A 就会把 B 设为PFAIL
状态,相当于主观下线 - A 判定 B为
PFAIL
之后,会通过redis
内置的Gossip
协议,和其他节点进行沟通,向其他节点确认 B的状态,每个节点都会维护一个自己的"下线列表",由于视角不同,每个节点的下线列表也不一定相同 - 此时A发现其他很多节点也认为B为
PFAIL
,并且数目超过总集群个数的一半,那么A就会把B标记成FAIL
(相当于客观下线),并且把这个消息同步给其他节点,其他节点收到之后,也会把B标记成FAIL
至此,B 就彻底被判定为故障节点了。
- 故障迁移
上述例子中,B 故障并且 A 把 B FAIL
的消息告知集群中的其他节点:
- 如果 B 是从节点,那么不需要进行故障迁移
- 如果 B是主节点,那么就会由 B 的从节点触发故障迁移
所谓故障迁移,就是指把从节点提拔成主节点,继续给整个redis
集群提供支持.具体流程如下:
- 从节点判定自己是否具有参选资格,如果从节点和主节点已经太久没通信,此时认为从节点的数据和主节点差异太大了,时间超过阈值,就失去竞选资格
- 具有资格的节点,就会先休眠一定时间,
休眠时间=500ms基础时间+[0,500ms]随机时间+排名*1000ms
,offset
的值越大,则排名越靠前(越小) - 如果某个节点的休眠时间到了,该节点就会给其他所有集群中的节点,进行拉票操作,但是只有主节点才有投票资格
- 每个主节点只有1票,当该节点收到的票数超过主节点数目的一半,就会晋升成主节点
- 新的主节点自己负责执行
slaveofno one
,并且让同一分片中的其它节点执行slaveof
- 最后,新的主节点会把自己成为主节点的消息,同步给其他集群的节点,大家也都会更新自己保存的集群结构信息
以上算法成为raft
算法,其实和哨兵选主的目的是一样的,就是选出那个目前网络状态比较好的节点成为主节点,而网络状态的反映,就是休眠时间。
有些情况下,如果节点宕机,会导致整个集群宕机,这称为fail
状态:
- 某个分片内部,所有的主节点和从节点都挂了
- 某个分片内部,主节点挂了没有从节点可以成为新的主节点
- 超过半数的主节点都挂了
集群扩容
- 加入集群
想要给集群扩容,可以通过--cluster add-node
选项:
redis-cli --cluster add-node 新增节点 集群任意节点
-
新增节点
:要增加到集群的节点 -
集群任意节点
:用于标识要加入哪一个集群
执行:
redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
这样就可以把xxx.110
节点加入到集群中,登入任意客户端查看:
可以看到,集群内部已经有xxx.110
了,而且是一个主节点。但是仔细观察,可以发现其他节点末尾都有哈希槽的范围,但是新增的节点没有,说明新节点还没有分配。
- 分配哈希槽
接下来就要给新节点分配哈希槽,同选项 --cluster reshard
,注意是shard
不是shared
。
redis-cli --cluster reshard 172.30.0.101:6379
执行后进入如下选项:
上面的S
表示从节点,M
表示主节点,在主节点的信息中,已经告知了每个主节点拥有的槽位个数。此处它询问要移动多少个slots
,也就是哈希槽。
此处要移动4096
个哈希槽给新节点,所以输入4096
:
随后它询问将这些哈希槽移动给哪一个节点,此时往上找哪一个master
节点的哈希槽为0
,复制他的ID。
最后询问,要从哪些节点中空出这些节点。如果选择all
,那么就是从所有的现有节点平均提取。如果你希望自己指定,那么就复制那些节点的ID,最后以done
结尾即可。
最后向用户确认,是否要这样执行。
最后进入任意客户端,查看集群现状:
可以看到,新节点获得了三个范围的哈希槽。
这里有一个小问题,这个搬运哈希槽的过程是比较久的,如果在搬运期间,用户访问数据是合法的吗?
这分情况,搬运过程中,大部分哈希槽是不用搬运的,如果用户访问这些哈希槽内的数据,那么可以正常访问。但是如果用户访问正在移动的哈希槽,那么就会失败了。
- 添加从节点
目前添加了主节点,最后还要把从节点安排上,这通过 add-node
命令配合--cluster-master-id
完成:
redis-cli --cluster add-node 新节点 --cluster-slave --cluster-master-id 主节点的ID
-
--cluster-slave
:这个选项指定新添加的节点将作为从节点 -
--cluster-master-id
:这个选项后面跟着的是主节点的ID,表示该节点从属于哪一个节点
此时从属节点就成功加入集群了。