可用性提升大作战-Redis的高可用策略之Sentinel

Redis高可用技术

说明

由于本人技术能力较为有限,无法支撑独立成文,本博客仅作为笔记以及学习用途,所有资料均来自参考资料中文章,请阅读者知悉。文章中可能有纰漏或者理解不到位的地方,希望各位尽情吐槽。

正文

有时候翻阅一些中间件的特性,感觉还是蛮有趣的。为了解决一个问题,各家提出不同的设计方案,尽管方案可能各有各的不同,但是细读下来却是颇具共性。本文尽管主要说明Redis的哨兵,但是在进入正题之前,又想聊聊Redis为保障高可用,给出的一些解决方案。

  • 持久化
  • 复制
  • 哨兵
  • 集群

以上这四种方案,笔者认为是一种递进具有层次的策略。姑且忽略里面的技术细节,你会发现对于存储而言,这四种策略是一种很朴素的想法。

单机Redis作为缓存中间件,首要的任务是就是要保证数据的可用性,尽管作为一款内存的缓存中间件,但是你不能说Redis一挂掉,就把数据给丢失。因此很直观的一个想法,就是将数据给持久化。但是就算持久化了,其实实际还不够的,因为作为单机的Redis可能发生硬件故障导致了单机不可用,那么很简单,一台机器无法保证,我们分开多台机器来降低硬件故障导致的数据丢失等情况。概率虽然降低了下来,但是问题是,往往在生产上,人是不可能一天二十四小时一直盯着机器的,那么我们就需要额外提供一种故障转移的方法,哨兵就是Redis官方给出的答案。然而随之而来的就是面对这么多机器,为了保障负载均衡,同时也为了提升Redis本身单机的存储能力的限制,集群应运而生。

本篇不想过多地介绍持久化、复制以及集群,毕竟在这简单地几个字里行间有着很多的技术细节。之后会另开章节一一介绍。
下面进入本篇的主题,Redis的哨兵机制。

1. 哨兵

Redis哨兵是为保障Redis主节点发生故障时能够及时监测并且实现自动故障转移的一种机制。

从上面的描述中,我们可以想象得到,类似的这种故障自动转移,需要能做到:

  • 故障发现
    监控到主节点和从节点是否运作正常
  • 故障自动转移(Automatic FailOver)
    主节点陷入故障状态时,能够及时地选择健康的从节点,令其升级,并替换失效的主节点。
  • 通知
    将故障转移后的结果通知给客户端

在这个过程中,我比较感兴趣的有几点:
(1)Redis的哨兵架构是怎么样的?
(2)面对众多节点,Redis的哨兵本身是如何发现主节点发生故障的?如何区分是自己故障还是主节点故障?
(3)Redis哨兵如果自己发生故障了,会有什么机制来保障本身的稳定性?
(4)在故障转移的过程中,主节点的数据是否和从节点的数据完全保持数据一致性?
(5)从节点升级为主节点的选举机制是什么?

以上的问题,都是一般故障转移所要面临的问题,那么Redis又是怎么去解决这些问题的呢?
带着这些问题,我们继续往下分析。

1.1 架构

图片来自:https://juejin.im/entry/5bda7d8af265da3942051253
备注:原本想自己画图的(灵魂画手的那种手工图),但是刚好身边ipad的笔不在,想用visio画又太懒了。毕竟是笔记向,一想到这,就心安理得把别人的图给拿来用了。之后再补图。图片水印csdn自己生成的,请教下如何删掉?
在这里插入图片描述
Redis的节点分为两种类型:

  • 哨兵节点
  • 数据节点

1.2 工作流程

1.2.1 健康检测

哨兵进程定时(一般是一秒钟一次)向主节点、从节点以及其他哨兵节点发送“Ping”命令,等待响应。如果在这个过程中,如果某个节点距离最后一次有效回复PING命令超过down-after-millseconds选项的值,则标记为主观下线(SDOWN)

1.2.2 主观下线

当某一个节点被标记为SDOWN,并不意味着该节点要立即下线,这时候其他监测该节点的其他哨兵节点,此时都会向该节点以每秒一次的频率进行确认。
这里可以认为主观下线是某一个节点认为数据节点存在问题,这是一种主观臆测,并不具备权威性,因此还需要咨询其他节点的意见。

1.2.3 客观下线

Sentinel节点间采用Gossip形式进行通信,如果一个哨兵节点在特定的时间内接收到别的哨兵节点的报告,说这个节点已经主观下线了,如果超过特定数量的节点认可了主观下线的结果了,此时则会将该节点标记为客观下线(ODOWN)
在这里有个细节,具体超过多少个哨兵节点的报告,才会认为一个数据节点是故障的,这里是通过sentinel monitor进行配置。

sentinel monitor <master-group-name> <ip> <port> <quorum>

在这里,quorum参数就是需要同意主节点不可用的Sentinel的数量。
那么我们可以通过调整quonum的大小来设置redis对故障的敏感度,理论上这个值越小,则会越发敏感。

1.2.4 选举

这里的选举包含两个动作,一个是选举谁来领导整个故障转移动作,另一个选举应该是谁来进行晋升。

第一个问题,选举谁来领导整个故障转移动作
当主节点被判断客观下线后,各个哨兵节点进行协商,选举出一个领导者哨兵节点,统一由该领导者对其进行故障转移。

那么这里就有个问题了,究竟这些哨兵节点是如何被授权做故障转移的。
Redis的哨兵节点的选举,其实是参考了当前分布式一致性协议Raft算法来做的。

由于本篇的侧重点在于Sentinel的设计思路,因此更加关注故障转移的流程,如果有童鞋想关注其中更多的技术细节,可以参考:Raft协议实战之Redis Sentinel的选举Leader源码解析

在Redis发起故障转移的流程后,一般说来,哨兵节点会进入以下流程:
(1)发起一个定时任务,该定时任务会去检查数据节点的状态(主观下线还是客观下线),如果已经到了客观下线,此时将会监测到故障的哨兵节点将会去开始发起投票阶段。
(2)此时该哨兵节点变更为候选人角色,开始竞选Leader,此时该候选人的哨兵节点会将自己的epoch加1,并且对外发送,同时给自己投上一票。其他哨兵节点在接收到这个请求后,这时候如果这个收到请求的哨兵节点之前未收到拉票的请求,此时会记录下自己认可的Leader,并返回。如果并非第一个请求,那么由于之前已经认可了别的节点为Leader了,那么是不做变更的。
(3)经过一轮投票后,开始确认自己是不是执行故障转移的Leader了。此时需要统计投票。要成为纪元内的Leader,需要满足:多余一半的哨兵节点支持;投票至少要有quorum那么多。如果统计完票数,满足上述的条件,则开始进入下一个流程。
(4)通知其他哨兵节点自己成为Leader.

1.2.5 故障转移

进入故障转移操作后,则需要从故障主节点下选择某个从节点晋升为主节点。
从节点晋升为主节点,需要评估以下的信息:

  • 与主节点断开的时间
  • 从节点的优先级(配置文件中slave-priority)
  • 复制偏移量
  • 运行ID

关于这四个要素,稍微展开说下
与主节点断开的时间将会作为一个过滤项,一个从节点被发现从主节点断开超过主节点配置超时(down-after-milliseconds)时间十倍以上,加上从正在执行故障转移的Sentinel角度看主节点不可用的时间,将被认为是不合适的并且被跳过。

与主节点断开的时间是作为一个过滤条件,如果被哨兵节点认为是不健康的数据从节点,直接就拒绝掉从节点的晋升资格。

之后再进行优先级的排序,具体的顺序如下:
(1)配置的slave-priority作为优先级考虑要素,更低的优先级会优先
(2)检查复制偏移处理,这一点应该能理解,偏移量更大的会认为从主节点收到更加新的数据,理论上也就更有资格获取到数据。
(3)如果优先级和偏移量都一样了,那么会选择一个更小的运行ID。

1.2.6 通知

所有哨兵节点会向所有的主节点发送INFO命令。
所有哨兵节点向下线节点的所有从节点发送INFO命令。

1.2.7 关于中断

如果没有足够多的哨兵节点统一对下线节点发起下线要求,此时会中断下线流程;同时如果在这个过程中,主节点回复响应,一样会中断流程。

1.3 算法

分布式一致性算法

了解Raft算法之前,我们首先要先了解一致性这个概念。在分布式的系统中,最经常遇到的就是一个很经典的问题,可用性与一致性要怎么形成权衡,要知道,为了保证可用性,我们可能会通过机器冗余,机器备份等方式防止机器崩溃而变得不可靠,这时候机器与机器的状态或者数据很容易就变得不一致。这种不一致如果涉及到多个节点(可能成千上万)将会让我们抓狂:我们该相信哪个节点的数据?
因此一致性协议(Consensus)应运而生。大佬们提出了Paxos算法,但是很可惜由于Paxos难以落地,难以理解(毕竟那是学神的世界,普通学渣无法触碰),此时各处学霸纷纷根据自己的理解,围绕分布式的一致性问题,提出了Raft算法、2PC、3PC算法。

而Redis解决节点的一致性问题,则是采用了Raft算法。尽管在本篇中不会详解Raft算法,但在此处作为Redsi的应用,我们也应该稍稍理解下Raft算法。

Raft算法
  • 角色划分

Raft算法总共分为三种角色:

  • Leader
    主要行为交互节点(如所有数据读写交互、日志复制)。
  • Follower
    负责响应来自Leader或者Candidate请求的角色。
  • Candidate
    选举Leader的一种角色。在系统正常运行时,该角色并不存在。当一个follower对Leader的心跳产生了异常,就会转变成Candidate。之后Candidate就会去竞选新的Leader。
    在这里插入图片描述
    理解Raft算法,需要理解以上三个状态的转换。

当一个节点在倒计时结束后,这个节点的状态将会变成候选人模式,此时它会自主地发送选举请求。这时候如果Follower都投赞成票,那么毫无疑问地候选人将会晋升为Leader。这是一种最简单的情况。
但是很多时候并不是如此,很多时候存在多个候选人,此时则会发起投票,此时对于Follower来说,往往会响应先来的请求,那么获取多票者当选为Leader。但是如果此时候选人获取的票是一致的,那么在一个term内候选人无法分出胜负,那么就只能等待timeout在下一轮进行拉票。

如果Leader出现故障后,候选人正常晋升为Leader,此时如果原来老的Leader又重新上线,这时候会出现什么情况呢?在这里提一个Term的概念,可以认为老的Leader是Term N的Leader,当他被篡位后,此时新的Leader将是Term N+1的Leader节点,那么对于其他的节点就会自动降为Follower状态。

分析整个Raft算法,个人认为主要是分析几个情况:

  • 正常情况从Follower转成候选人的时候,进行晋升Leader的选举。
  • Leader出故障下的Follower转成候选人进行选举。
  • Leader出故障下的Follower后成功选举出Leader后,老的Leader重新恢复正常。
  • 多候选人选举时拉到不同的票;
  • 多候选人选举时获取到相同的票;

1.4 缺陷

1.4.1 关于脑裂

尽管Redis提供了哨兵的模式来提升可用性,但是实际上,并非完全没有问题的。
这里有个很经典的问题,就是脑裂问题(分布式数据存储都有类似的问题)。
那么脑裂问题是如何触发的呢?
(1)需要本身主节点机器短暂的脱离联络,与其他从节点机器和哨兵节点都失去联系,但是实际主节点还在运行。部分应用还在使用该主节点
(2)此时哨兵发现故障,进入选举过程,并将某个从节点晋升为主节点,此时部分应用接入新的主节点。但是仍有部分应用仍使用旧的主节点,未切向新的主节点。
(3)网络恢复后,旧有的主节点重新作为从节点挂在新的主节点,此时会发生数据丢失的问题。

1.4.2 异步复制的数据丢失问题

从主节点同步到从节点,是以异步的方式进行同步的,无法保证实时同步数据,那么对于主节点来说,一旦宕机了,这时候这部分数据就会丢失了。

其他解决方案

未完待续

参考资料

Redis Sentinel Documentation
实现故障恢复自动化:详解Redis哨兵技术
Redis哨兵模式
解读Raft
共识算法:Raft
Redis脑裂问题分析
Raft协议实战之Redis Sentinel的选举Leader源码解析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值