1、Redis Cluster 介绍
使用哨兵sentinel 只能解决Redis高可用问题,实现Redis的自动故障转移,但仍然无法解决Redis Master单节点的性能瓶颈问题。
为了解决单机性能的瓶颈,提高Redis 服务整体性能,可以使用分布式集群的解决方案
早期 Redis 分布式集群部署方案:
- 客户端分区:由客户端程序自己实现写入分配、高可用管理和故障转移等,对客户端的开发实现较为复杂。
- 代理服务:客户端不直接连接Redis,而先连接到代理服务,由代理服务实现相应读写分配,当前代理服务都是第三方实现。此方案中客户端实现无需特殊开发,实现容易,但是代理服务节点仍存有单点故障和性能瓶颈问题。比如:豌豆荚开发的 codis
Redis 3.0 版本之后推出无中心架构的 Redis Cluster ,支持多个master节点并行写入和故障的自动转移动能
2、Redis cluster 架构
2.1、Redis cluster 架构
![图片[1]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-198.png)
Redis cluster 需要至少 3个master节点才能实现,slave节点数量不限,当然一般每个master都至少对应的有一个slave节点
如果有三个主节点采用哈希槽 hash slot 的方式来分配16384个槽位 slot
此三个节点分别承担的slot 区间可以是如以下方式分配
节点M1 0-5460
节点M2 5461-10922
节点M3 10923-16383
![图片[2]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-199.png)
2.2、Redis cluster的工作原理
![图片[3]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-200.png)
数据分区
如果是单机存储的话,直接将数据存放在单机redis就行了。但是如果是集群存储,就需要考虑到数据分
区了。
![图片[4]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-201.png)
数据分区通常采取顺序分布和hash分布。
![图片[5]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-202.png)
顺序分布保障了数据的有序性,但是离散性低,可能导致某个分区的数据热度高,其他分区数据的热度低,分区访问不均衡。
哈希分布也分为多种分布方式,比如区域哈希分区,一致性哈希分区等。而redis cluster采用的是虚拟槽分区的方式。
虚拟槽分区
redis cluster设置有0~16383的槽,每个槽映射一个数据子集,通过hash函数,将数据存放在不同的槽位中,每个集群的节点保存一部分的槽。
每个key存储时,先经过哈希函数CRC16(key)得到一个整数,然后整数与16384取余,得到槽的数值,然后找到对应的节点,将数据存放入对应的槽中。
![图片[6]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-203.png)
集群通信
但是寻找槽的过程并不是一次就命中的,比如上图key将要存放在14396槽中,但是并不是一下就锁定了node3节点,可能先去询问node1,然后才访问node3。
而集群中节点之间的通信,保证了最多两次就能命中对应槽所在的节点。因为在每个节点中,都保存了其他节点的信息,知道哪个槽由哪个节点负责。这样即使第一次访问没有命中槽,但是会通知客户端,该槽在哪个节点,这样访问对应节点就能精准命中。
![图片[7]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-204.png)
- 节点A对节点B发送一个meet操作,B返回后表示A和B之间能够进行沟通。
- 节点A对节点C发送meet操作,C返回后,A和C之间也能进行沟通。
- 然后B根据对A的了解,就能找到C,B和C之间也建立了联系。
- 直到所有节点都能建立联系。
集群扩容
- 当有新的节点准备好加入集群时,这个新的节点还是孤立节点,加入有两种方式。一个是通过集群节点执行命令来和孤立节点握手,另一个则是使用脚本来添加节点。
- cluster_node_ip:port: cluster meet ip port new_node_ip:port
- redis-trib.rb add-node new_node_ip:port cluster_node_ip:port
- 通常这个新的节点有两种身份,要么作为主节点,要么作为从节点:
- 主节点:分摊槽和数据
- 从节点:作故障转移备份
![图片[8]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-205.png)
其中槽的迁移有以下步骤:
![图片[9]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-206.png)
集群缩容
![图片[10]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-207.png)
下线节点的流程如下:
- 判断该节点是否持有槽,如果未持有槽就跳转到下一步,持有槽则先迁移槽到其他节点
- 通知其他节点(cluster forget)忘记该下线节点
- 关闭下线节点的服务
需要注意的是如果先下线主节点,再下线从节点,会进行故障转移,所以要先下线从节点。
故障转移
除了手动下线节点外,也会面对突发故障。下面提到的主要是主节点的故障,因为从节点的故障并不影响主节点工作,对应的主节点只会记住自己哪个从节点下线了,并将信息发送给其他节点。故障的从节点重连后,继续官复原职,复制主节点的数据。
只有主节点才需要进行故障转移。在之前学习主从复制时,我们需要使用redis sentinel来实现故障转移。而redis cluster则不需要redis sentinel,其自身就具备了故障转移功能。
根据前面我们了解到,节点之间是会进行通信的,节点之间通过ping/pong交互消息,所以借此就能发现故障。集群节点发现故障同样是有主观下线和客观下线的
主观下线
![图片[11]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-208.png)
对于每个节点有一个故障列表,故障列表维护了当前节点接收到的其他所有节点的信息。当半数以上的持有槽的主节点都标记某个节点主观下线,就会尝试客观下线。
客观下线
![图片[12]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-209.png)
故障转移
集群同样具备了自动转移故障的功能,和哨兵有些类似,在进行客观下线之后,就开始准备让故障节点的从节点“上任”了。
首先是进行资格检查,只有具备资格的从节点才能参加选举:
- 故障节点的所有从节点检查和故障主节点之间的断线时间
- 超过cluster-node-timeout * cluster-slave-validati-factor(默认10)则取消选举资格
然后是准备选举顺序,不同偏移量的节点,参与选举的顺位不同。offset最大的slave节点,选举顺位最高,最优先选举。而offset较低的slave节点,要延迟选举
![图片[13]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-210.png)
当有从节点参加选举后,主节点收到信息就开始投票。偏移量最大的节点,优先参与选举就更大可能获得最多的票数,称为主节点
![图片[14]-Redis Cluster-李佳程的个人主页](http://www.lijiach.com/wp-content/uploads/2022/11/image-211.png)
当从节点走马上任变成主节点之后,就要开始进行替换主节点:
- 让该slave节点执行slaveof no one变为master节点
- 将故障节点负责的槽分配给该节点
- 向集群中其他节点广播Pong消息,表明已完成故障转移
- 故障节点重启后,会成为new_master的slave节点