漫画解读redis集群|哨兵|脑裂
哨兵
还是拿现实生活举例,假设某小公司暂存资料的
柜子
,有多个格子,柜子还有一个专门的管理员
,他的工作就是按公司员工
的命令在某个格子(key)存资料(value),取资料(value)。
管理员
+柜子
就相当于redis,员工
向柜子管理员发命令存取资料,所以每个员工
相当一个个客户端。
但公司的管理员经常跑肚拉稀上厕所,导致无法提供服务
redis服务可能因为网络问题,服务器问题导致宕机
为了应对这种问题,公司采取主从方案,给
主管理员
配置多个从管理员
,原管理员升职为主管理员
,他们的职责是这样的,主管理员
提供存取服务和原来一样,每个从管理员
也掌管一个新柜子,并且主柜子里有什么他们就复制一份存入自己的柜子,和主管理员
的资料实时同步,而且可以分担一部分的取资料任务。
这就是redis的主从架构,一个主节点配几个个从节点,从节点实时同步主节点的数据,主节点可以承接读写命令,从节点可以承接读命令,至于如何主从如何同步数据移步上一篇文章
这样做确实可以
主管理员
分担压力,但是当主管理员
上厕所时候还是无法提供存资料的服务,因为从管理员
只承接取资料任务不能存资料。
单纯的主从架构,主节点挂了,redis就不能set了
所以需要一个方案,能在
哨兵模式主管理员
不在岗的时候,选一个从管理员
升级为主管理员
。为此公司组建了个监控团队
,他们负责监控所有柜子管理员,如果主管理员
不在岗,他们负责选出一个新主管理员
,该方案起名叫哨兵方案
这个监控团队
就是哨兵集群,哨兵集群由多个哨兵服务组成,sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点,主节点挂了选举出一个新的主节点并告知客户端。
由于本文主要讨论集群,哨兵就简单介绍一下。
集群
公司的这个
哨兵方案
有个大问题,就是不管多少个从管理员
,负责存资料的只有一个主管理员
,如果公司越来越大,存资料人多了,他会应付不过来,而且资料越来越多,柜子不断变大,会导致从管理员
复制资料越来越慢。
Redis哨兵架构中只有主节点负责写命令,如果并发量很大,执行等待时间会变长,而且redis内存不宜过大(尽量10G以下),要不增加主从复制和持久化的压力,两种情况都会导致redis可用性就会降低,所以并发量特大或需缓存数据特别多情况下哨兵架构表现就很一般。
公司越来越大,人和资料越来越多,所以提出一个新方案:
集群集群方案
。
方案其实很简单,人多了,多上几个柜子分开存不就就可以了吗,具体方案如下:
多增几组柜子
和管理员
,分别存放不类型的资料,由于每个柜子存放的资料不同,所以为了防止管理员
跑肚拉稀,还是留着主从管理员这套体系,一个主从体系取名叫一个柜子小组
,分A、B、C组,多个柜子小组
共同提供存取资料服务。
redis常用的集群架构就是多个主从组合成一个大集群,这样解决了高并发时的反应速度问题,把压力分散给不同节点,每个节点也不用存储太多数据,例如需要存30G数据,三个节点每个节点存10G就可以了。
高可用集群模式
槽位定位算法
问题来了,这么多组
定位柜子
+管理员
,怎么分配工作,即什么样的文件存在什么柜子里。
解决思路很简单,分类就可以了,比如公司当前就三组柜A,B,C,那么就给公司所有资料分为A,B,C类,A类存A柜、B类存B柜、C类存C柜即可。每个员工手上都有一个类型和柜子的对照图,避免存错柜子。
但是这样会有问题,比如公司又壮大了,增加了个D组柜,那D组柜放什么资料?没资料可放了...,加了也白加,或者给资料重新分类,但是太麻烦。
于是公司预估了一下按发展未来可能最多会上16384个柜,那么就给资料分16384类。
分类方法比如可以按资料的编号(假设纯粹数字而且很大,是16384的好几倍)对16384取模,得到的范围0-16383的资料类型值
。
这样就实现了对所有资料分16384类,然后给每个柜子小组
分配多个类型的任务,比如类A组0-4999
类,B组5000-9999
类,C组10000-16383
类,将来来了个D组,从这些组取一部分类区间给D,D组柜子就实际派上用场了。
Redis Cluster采用槽位定位算法,将所有数据划分为 16384 个slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。
Cluster默认会对key值使用crc16算法进行hash
得到一个整数值,然后用这个整数值对16384进行取模来得到具体槽位。16384个槽位可以看出集群的节点最多也就1万多个,再多可能就分不到任务了,官方推荐是1000以下。
每个公司
分布图员工
首先要知道各柜子小组
负责的资料类型分布,简称分布图
,才能知道要去哪存,这个分布图
每个柜子小组
都有一份,一般员工得到后会用一张纸记录一下,省着总去问。
客户端要访问redis集群首先要拿到槽位的信息,然后才能根据key
的运算知道具体要访问的节点,这个槽位的信息每个节点都持有,可以从任何一个节点获取,获取之后一般要暂存一份,避免每次都重新获取。
如果
分布图
过期了、错了,导致定位错了柜子小组
,柜子小组
的管理员
会告诉员工
正确的柜子
是哪个,比较聪明的员工
出现这种问题,会知道自己的分布图
不准确,会重新要一份更新自己的分部图
。
当redis客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据
重定向
此时客户端合理的操作会重新获取一下槽位的信息
jedis|spring
通过上面的描述会发现,需要
员工
做的事太多了:1.记下分布图
,2.计算文件类型值, 3.根据分布图
和计算的文件类型值,去找对应的柜子小组
存取资料,4.如果发现找错了柜子小组
,重新获取分布图
,重新定位柜子小组
。
程序员连接redis集群需要负责如下逻辑:
- 缓存槽位分布图
- 计算槽位
- 根据分布图定位节点,发送请求
- 出现错误更新槽位分布图,重新定位节点发请求
写个代码太累了。。。
好在这些或都被第三方工具封装好了,比如jedis和spring,他们内部屏蔽掉了这些事情,所以我们代码只负责存取即可,和操作单机redis一样一样的。
gossip
各
柜子小组
之间,需要互相通信,每个管理员
彼此保留联系方式,并长期保持通话,可以想象为大家建立了一个群聊,彼此通信,因为没有一个统一的指挥者,也不像哨兵方案
那样有监控者
,所以需要各小组互相协调。
redis集群的多个节点都会互相通信,这样才能感知彼此,同步槽位信息,重定向等功能,有点像去中心化的区块链,通信协议gossip有点复杂,有空单独研究。
选举
竞选
柜子小组
中,每个主管理员+柜子(简称主柜
)都配一两个从管理员+柜子(简称从柜
),保留主从体系,这样当主柜
不在岗了,会有一个从柜
被重新竞选为主柜
,以保证所有柜子小组
都可用。
在集群方案
中,从柜
提供的读功能就比较鸡肋了,所以集群方案中从柜
只负责复制数据和随时准备顶替主柜
。
上面介绍过哨兵方案
中由监控人员
来选举出新的主,但新的集群方案并不存在这个角色,那是如何进行选举的呐?方案如下:
假如A组主柜
管理员不在了,那本组的两个从柜
管理员开始竞争上岗,他们分别联系其他组的主管理员
,其他组的主管理员会给第一个联系上他的竞选者投一票,如果竞选者收到的投票超过小组数的一半,则成功当选。如果俩人平分秋色,或者都没达到要求,那么重新选举,直到选出为止。
选举
为了避免选的次数太多,公司规定每个竞选者进行选举前需要等一会,具体等待的时间跟复制的资料新旧有关,资料越新代表他的复制工作做的越好,更能还原前任主柜
的资料,那么对应的竟选择等待的时间就短,竞选成功的概率就大。
但是又出问题了,如果俩竞选者资料一样新,可能又会出现多次选举失败浪费时间,所以在看资料新旧的基础上,再加一个随机抽数环节,比如弄个抽奖箱,里面放多个数字,抽中几就等几秒,这样大大减少了选举的次数。
所以还原度越高的从柜
不一定当选,但被选中的几率相对更大。
Redis Cluster 中,当slave发现自己的master变为FAIL状态时,便尝试进行选举,以期成为新的master。其过程如下:
- slave广播竞选信息
- 其它master收到信息,回应ack,每个主节点每轮选举只回应一次ack
- slave收集master返回的ack
- slave收到超过半数master的ack后变成新master
- slave广播pong消息通知其他集群节点竞选成功。
slave并不是在master一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟。
延迟计算公式:
DELAY = 500ms + random(0 ~ 500ms) + slave_rank* 1000ms
- slave_rank
slave已复制的数据越新,slave_rank值越小,延迟越低 - random(0 ~ 500ms)
加一个随机数,避免多slave的slave_rank相同重复选举
小补充
1.为什么集群主节点至少三个?
因为需要半数以上票数才能竞选成功,集群的半数机制,所以集群至少三个主节点,因为如果有俩个主节点,一个挂了,选举最多只能收一票,不大于主节点数的一半,所以永远不会竞选成功。
2.集群是否完整才能对外提供服务?
加入一个主节点挂了,且没有从节点可替换,那么这组的槽位数据访问不了了,但其他的主节点原则应该也可以继续访问,那么具体可不可以访问呐?答案是:可配,配置项是cluster-require-full-coverage
脑裂
回到例子,还是A,B,C三组,所有组的所有成员彼此通信,有一天出现了问题,A组的
脑裂主管理员
由于一些原因,联系不上其他人了,其他人也都发现A主柜
联系不上了,于是从新从A组中重新选出了一个主柜
。
这样一来有些知道变更的员工就去新主柜
存取资料,但还有一些不知道变更的员工还会取旧主柜
存取资料,导致出现了两个主柜
在这期间各存各的。
如图,绿色
和蓝色
是两个不同的办公区,中间有个大铁门突然被锁住了,导致彼此不能通信,A组的原主
联系不上其他管理员,但是还得继续给绿区员工
提供服务。其它管理员也联系不上A组原主
,只能推选一个新的主柜
,给黄区员工
提供服务,这样同一逻辑组A实际上就共同存在了两个主。
过了一段时间,门打开,彼此互相可以通信了,原主柜
发现集群有新主柜
了。。。没办法,只能自己充当从柜
,清空自己的资料去同步复制新主柜
的资料,这样就导致隔离这段期间原主柜
新增的资料消失了。
脑裂恢复丢失数据
redis集群会有脑裂问题,网络分区导致脑裂后多个主节点对外提供写服务,一旦网络分区恢复,会将其中一个主节点变为从节点,这时会有大量数据丢失。
为了应对这种情况,公司针对一主双从提出了一个方案,每次
主管理员
存资料时,至少一个从管理员
完成同步复制才算存储成功,否则告诉员工
失败,这样就可以避免脑裂问题:
当出现某主柜
和其他所有管理员通信断了的情况,主柜
由于联系不到任何从柜
无法提供存资料服务。
如果主柜
携带某个从柜
一起和其它管理员断了通信,由于两个从柜
一个能找到主,一个不能找到主,不符合半数机制,不能发起重新选举。
对应的如果5个从柜可以让其中3个确认才算成功,以此类推。
redis规避脑裂的方法:可以在redis配置里加上参数(这种方法不可能百分百避免数据丢失,参考集群leader选举机制):
min-replicas-to-write 1 //写数据成功最少同步的slave数量,这个数量可以模仿大于半数机制配置,比如集群总共三个节点可以配置1,加上leader就是2,超过了半数
注意:这个配置在一定程度上会影响集群的可用性,比如slave要是少于1个,这个集群就算leader正常也不能提供服务了,需要具体场景权衡选择。
over~画图不易,如果对你有帮助,给个赞吧!