一.主库挂了,如何不间断服务?
主库挂了,需要运行一个新的主库:将从库切换为主库。这就涉及到三个问题:
主库真的挂了吗?
选择哪个从库作为主库?
如何把新主库相关信息通知给从库和客户端
哨兵是实现主从库自动切换的关键机制,有效解决主从复制模式下故障转移的上面这些问题。
二.哨兵机制的基本流程
哨兵是一个运行在特殊模式下的Redis进程,主从库实例运行时,他也在运行。
哨兵负责三个任务:监控,选主(选择主库)和通知。
监控
监控是指哨兵进程运行时,周期性给所有主从库发送PING命令,检测他们是否仍然在线运行。
从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为"下线状态";
主库没有在规定时间呢响应哨兵的PING命令,哨兵就会判定主库下线启动选主流程。
选主
一定规则从从库中选出作为新的主库。
通知
新主库建立连接,复制数据。同时,哨兵会把新主库的连接信息通知给客户端,让它们将操作请求发送给新主库上。
通知任务简单,哨兵只需要把新主库信息发给从库和客户端。通知他们和新主库建立连接就成,不涉及决策逻辑。
监控和选主需要哨兵做出决策:
判断主库是否处于下线状态;
选主哪个从库实例作为主库。
二.主管下线和客观下线
使用PING命令检测主和从库的连接情况,用来判断实例状态;
哨兵就会把它标记为"主观下线"。
从库,哨兵可以简单标记为"主观下线",因为从库下线影响不大,集群对外服务不会中断。
哨兵误判,主库没有故障,可是一旦启动选主和通知操作后续的选主和通知操作都会带来额外的计算和通信开销。还可能产生脑裂。
什么是误判
集群网络压力较大,网络拥塞,主库本身压力较大。
一旦哨兵判断主库下线,就会开始重新选择主库,并让从库和新主库进行数据同步,这个过程中本身就会有开销。
哨兵也要花时间选出新主库。因此我们需要减少误判。
少数服从多数
哨兵集群:哨兵多实例组成的集群模式进行部署。引入哨兵实例判断,避免单哨兵因为自身网络状况不好,误判主库下线的情况。同时,多搜啊并网络同时不稳定的概率较小,它们一起决策让误判率降低。
只有大多数哨兵实例判断主库都已经"主观下线",主库才会被标记"客观下线"-----少数服从多数
"客观下线":N个哨兵实例,最好要有N/2+1个实例判断主库为"主观下线",才能判断为"客观下线"。
减少误判的概率,避免误判带来无谓的主从切换。
三.选定新主库
筛选+打分
筛选条件:
检查从库的当前在线状态,判断他之前的网络连接状态
如果从库总是和主库断连,断连次数超过一定阈值,该从库网络状态不好。
判断方式:
down-after-milliseconds*10。down-after-milliseconds是我们认定主从库短链的最大连接超时时间。
断连次数超过10次,说明从库网络状态不好,不适合作为新主库。
从库打分:
按照三个规则依次进行三轮打分,这三个规则分别是从库优先级,从库复制进度和从库ID号。只要在某一轮中从库得分最高,他就是主库了。选主过程结束。如果没有出现得分最高的从库,那就会继续进行下一轮。
第一轮:优先级最高的从库得分高
配置slave-priority配置项,给不同从库设置不同优先级。比如两个从库内存大小不一样,可以手动设置内存大的实例设置为一个高优先级。选主时候哨兵会选出优先级最高的打高分作为新主库,如果得分一直,那就开始第二轮打分。
第二轮:和旧主库同步程度最接近的从库得分高
如果选择与旧主库同步最接近的从库作为主库,那么新主库上就有最新的数据。
如何判断从库和旧主库间的同步进度?
slave_repl_offset最接近旧主库的master_repl_offset,那么它的得分最高,可以作为新主库。
如果两个从库的slave_repl_offset值大小一样,那么就需要进入第三轮打分了。
第三轮:ID号越小得分越高
每个实例在创建时候都会有一个ID,目前Redsi在选主从库时,有一个默认的规定:在优先级和复制进度都相同的情况下,ID号最小的从库得分最高,会被选为新主库。选主完成!
课后提问:
哨兵机制可以实现主从库的自由切换,这是实现服务不间断的关键支撑,主从切换是需要一定时间,在切换过程中客户端是否可以正常的进行请求操作,如何不感知的实现灰度切换。
读写分离,那么读请求可以在从库上正常执行,不会受到影响。但是由于主库已经挂了,哨兵还没选出新主库,这时候写请求会失败,失败时间=哨兵切换主从时间+客户端感知到新主库时间。
方案1:
写失败的请求缓存或者写入消息队列中间件中,等哨兵切换完主从后,再执行这些命令,但是这种场景只适合对写入请求返回值不敏感的业务,而且还需要业务层做适配。如果主从切换时间过长,导致客户端或者消息队列中间件缓存写请求过多,切换完成后重放请求时间过长。
方案2:
down-after-milliseconds参数;
配置时间越短,哨兵越敏感,可能会导致误判。但是当主库真正故障,因为切换及时,对阢影响最小,如果配置时间越长,哨兵越保守,可以较少哨兵误判概率,主库故障发生时,业务写失败的时间也会较长。
方案3;
哨兵通知客户端,让客户端能够及时感知主库变化,将缓存的写请求写入新库中,保证后续写请求不会收到影响。
pubsub。客户端订阅这个主题,当pubsub有数据时将最新主库地址推送给客户端,写请求写到这个新主库即可,这种机制属于哨兵主动通知客户端。
如果客户端因为某些原因错过推送通知,客户也需要主动回去这个主题信息。
需要从哨兵集群中获取最新地址(sentinel get-master-addr-by-name命令),这样哨兵切换后,客户端可以从哨兵集群中拿到最新的实例地址。
一般Redis的SDK都提供了通过哨兵拿到实例地址,再访问实例的方式,我们直接使用即可,不需要自己实现这些逻辑。对于只有主从实例的情况,客户端需要和哨兵配合使用,而在分片集群模式下,这些逻辑都可以做在proxy层,这样客户端也不需要关心这些逻辑了。