在上一篇读写分离支撑qps10w+
,讲到使用redis读写分离,能够通过增加slave,来成倍的增加读操作的qps。
但是如何保证主从架构承受99.99%的高可用性?
- 系统可能随时挂掉
- 机器死了,宕机了
- jvm进程oom了,挂了
- 磁盘满了,不工作了, 系统各种io报错
- 4个9:系统可用的时间 / 系统故障的时间,在365天 * 99.99%的时间内,你的系统都是可以正常对外提供服务的,那就是高可用性,99.99%
- 什么叫redis不可用:master死了,不能写数据了,就相当于redis是不可用了。
- redis如何做到高可用
- redis高可用,叫故障转移,failover,也可以叫主备切换
- 在master node故障时,自动检测,并且将某个salve node 自动切换为master node的过程,叫主备切换,这个过程,实现了redis的主从架构下的高可用性。
- 一旦出现故障,在很短的时间,就会切换到另一个master上,可能就几分钟,几秒钟,redis不可用
所以,需要在主从复制读写分离中引入sentinel哨兵机制,哨兵是用来监测master和slave状态的工具。
如果检测到master宕机,马上就会通过一系列的选举操作,在slave中选出一个节点来顶替master的工作,
让系统继续保持运转。
本文从以下几点来介绍哨兵:
- 1、哨兵的介绍与几个基础问题
- 2、哨兵的工作流程以及7个核心底层原理
- 3、两个主备切换过程中的数据丢失问题
- 4、实战:手动搭建3个节点主备集群+哨兵(附带redis配置和docker启动脚本)
- 5、sentinel容灾演练
- 6、总结
1、哨兵的介绍与几个基础问题
1.介绍
- sentinal ,中文名哨兵
- 哨兵是redis集群架构中非常重要的一个组件,主要功能如下
-(1)集群监控,负责监控redis master和slave进程是否正常工作
-(2)消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
-(3)故障转移,如果master node挂掉了,会自动转移到slave node上
-(4)配置中心,如果故障转移发生了,通知client客户端新的master地址 - 哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作
-(1)故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
-(2)即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了
2.哨兵的注意事项
- (1)哨兵至少需要3个实例,来保证自己的健壮性。
- 因为到master挂了,需要每个哨兵投票选一个新master。
- 到达一定数量的哨兵投给了一个slave才能进行切换。所以太少了不能满要求。
- (2)哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
- 因为当一个master写了数据,还没来得及同步就挂了,那么这个数据无法保证不丢
- 哨兵只能保证master挂了,能选出新的slave,成为master,继续提供服务
- 数据丢失问题下面会有讨论。
- (3)对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练
3.经典的三节点哨兵集群。
- 配置:
quorum = 2
,majority=2
- quorum:判定master宕机的最少哨兵数量,主要是确认master是否真的宕机
- majority:大多数哨兵的数量。3个节点就是2,4个节点也是2,5个节点就是3,7个就是4
- master真宕机以后, majority选了一个slave节点当master才能进行主备切换
- 如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举出一个来执行故障转移
- 同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移
2、哨兵的工作流程以及7个核心底层原理
sdown:subjective down,主观宕机,当一个哨兵认为master挂了,就是sdown
odown:objective down,客观宕机,当超过quorum个哨兵认为master挂了,就是odown
哨兵工作流程
- 每个哨兵都会定时与master通信,相互之间也会通信,相当于是监视每个节点的工作情况
- 当有一个master挂了,认为master sdown了
- 然后当有quorum个哨兵认为master sdown了,那master就odown了。
- 当master odown了,哨兵就需要选举一个slave成为新的master
- 选举成功以后,就会逐个修改每个节点的配置,让集群重新运转起来。
7个底层核心原理
-
1.sdown和odown转换机制
- 一个哨兵认为master挂了就是sdown
- quorum个哨兵认为master sdown了,master就是odown了
- sdown达成的条件很简单,如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机
-
2.哨兵集群的自动发现机制(主要通过pub/sub发布订阅的方式,监控每个节点)
- 哨兵互相之间的发现,是通过redis的pub/sub系统实现的,每个哨兵都会往sentinel:hello这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在
- 每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的sentinel:hello channel里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置
- 每个哨兵也会去监听自己监控的每个master+slaves对应的sentinel:hello channel,然后去感知到同样在监听这个master+slaves的其他哨兵的存在
- 每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步
-
3.slave->master选举算法
- 如果一个master被认为odown了,而且majority哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来
- 会考虑slave的一些信息
- (1)跟master断开连接的时长
- 如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master
- (2)slave优先级
- 按照slave优先级进行排序,slave priority越低,优先级就越高
- (3)复制offset
- 如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高
- (4)run id
- 如果上面两个条件都相同,那么选择一个run id比较小的那个slave
- (1)跟master断开连接的时长
-
4.slave配置的自动纠正(当新选出master ,就会对每个节点配置自动纠正)
- 当slave选举为master,然后就修改每个节点的配置
- 如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保它们连接到正确的master上
-
5.quorum和majority
- 每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换
- 如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切
- 但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换
-
6.configuration epoch (修改配置过程中干的事情)
- 哨兵会对一套redis master+slave进行监控,有相应的监控的配置
- 执行切换的那个哨兵,会从要切换到的新master(slave->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的
- 如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号
-
7.configuraiton传播
- 哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub消息机制
- 这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的version号的
- 其他的哨兵都是根据版本号的大小来更新自己的master配置的
3、两个主备切换过程中的数据丢失问题
(1).异步复制导致数据丢失现象
- 因为master -> slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了
(2).脑裂导致的数据丢失
- 脑裂,也就是说,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master,这个时候,集群里就会有两个master,也就是所谓的脑裂
- 此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了
- 因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据
![集群脑裂导致的数据丢失问题.png](https://upload-images.jianshu.io/upload_images/21341666-fbe76cd46b5d070c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
解决异步复制和脑裂导致的数据丢失:在master新增如下两个配置
min-slaves-to-write 1
min-slaves-max-lag 10
上面的意思是,至少有1个slave,数据复制和同步的延迟不能超过10秒。
一旦所有的slave,数据复制和同步的延迟都超过了10秒,那么master就不接受数据了。-
减少异步复制的数据丢失
- 有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内
- 当master拒绝了client端的写操作,那么client就会在本地缓存,放入mq之类的,等master恢复后再往master写。
- 这样master在恢复以后,就最多丢10秒数据。
-
减少脑裂的数据丢失
- 和上面类似,master脑裂后,和slave失去联系,那么就不会接收client数据,client本地缓存,也能减少数据损失
- 因此在脑裂场景下,最多就丢失10秒的数据
4、实战:手动搭建3个节点主备集群+哨兵(附带redis配置和docker启动脚本)
sentinel.conf
daemonize no
protected-mode no
port 26379
dir "/data"
logfile "sentinel.log"
sentinel monitor mymaster 192.168.5.10 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel failover-timeout mymaster 180000
sentinel auth-pass mymaster 123456
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
docker 启动sentinel的指令
docker run -d --name sentinel --restart=always --net=host -p 26379:26379 -v /data/redis:/data redis redis-sentinel /data/sentinel.conf
步骤:
- 首先参照上一篇文章,搭建好三节点的redis主从集群(//www.greatytc.com/p/d3a0cc584be2)
- 然后在三个节点,每个节点的
/data/redis
下新建一个sentinel.conf
,然后把上面哨兵配置复制进去,注意ip端口修改为自己master的ip和端口 - 在每台机器上启动sentinel,李荣上面docker启动命令
- 观察每个
/data/redis
下面的sentinel.conf
文件,会多出监测到的哨兵节点
5、sentinel容灾演练
步骤:
- 当redis集群和sentinel集群都搭建完毕
- 在两台从节点上,
tail -f sentinel.log
观察哨兵日志文件 - 然后在主节点上关闭一个master节点:
docker stop redis
- 观察另外两个哨兵日志,几秒后,会打印master sdown , 然后选举新master的过程
- 然后再去新的master上set key value, 在从节点上能获取到就说明容灾测试成功。
6、总结
- 为了保证4个9的高可用性,所以在redis集群上引入了哨兵
- 哨兵能在master挂掉的时候,快速发现,并选举新的master重新提供服务。
- 采用
min-slaves-to-write 1
,min-slaves-max-lag 10
这两个配置,能有效保证集群最多丢失10秒数据