Redis哨兵实现原理
本文内容出自李子骅的《Redis入门指南》
一、读取配置文件内容
一个哨兵进程启动时会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:
sentinel monitor master-name ip redis-port quorum
- master-name是一个由大小写字母、数字和'.','-','_'组成的主数据库的名字。
因为考虑到故障恢复后当前监控的系统的主数据库的地址和端口会产生变化,所以哨兵提供了命令可以通过主数据库的名字获取当前系统的主数据库的地址和端口号。 - ip表示当前系统中主数据库的地址,而redis-port则表示端口号。
- quorum用来表示执行故障恢复操作前至少需要几个哨兵节点同意,作用可查看下文。
一个哨兵节点可以同时监控多个Redis主从系统,只需要提供多个sentinel monitor配置即可,例如
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel monitor othermaater 192.168.1.3 6380 4
同时多个哨兵节点也可以同时监控同一个 Redis主从系统,从而形成网状结构。
配置文件中还可以定义其他监控相关的参数,每个配置选项都包含主数据库的名字使得监控不同主数据库时可以使用不同的配置参数。例如:
sentinel down-after-milliseconds mymaster 60000
sentinel down-after-milliseconds othermaster 10000
上面的两行配置分别配置了mymaster和othermaster的down-after-milliseconds选项分别为60000和10000。作用可查看下文
二、建立连接
哨兵启动后,会与要监控的主数据库建立两条连接,这两个连接的建立方式与普通的Redis客户端无异。
-
其中一条连接用来订阅该主数据的
sentinel:he11o
频道以获取其他同样监控该数据库的哨兵节点的信息 -
使用另外一条连接来定期向主数据库发送Info等命令来获取主数据库本身的信息。
之所以哨兵会使用另一条连接发送这些命令,是因为当客户端的连接进入订阅模式时就不能再执行其他命令了
三、哨兵的三个定期操作
和主数据库的连接建立完成后,哨兵会定时执行下面3个操作:
- 每10秒哨兵会向主数据库和从数据库发送INFO命令。
- 每2秒哨兵会向主数据库和从数据库的
sentinel:he11o
频道发送自己的信息。 - 每1秒哨兵会向主数据库、从数据库和其他哨兵节点发送PING命令
这3个操作贯穿哨兵进程的整个生命周期中,非常重要,可以说了解了这3个操作的意义就能够了解哨兵工作原理的一半内容
-
发送INFO命令使得哨兵可以获得当前数据库的相关信息(包括运行ID、复制信息等)从而实现新节点的自动发现。
前面说配置哨兵监控Redis主从系统时只需要指定主数据库的信息即可
因为哨兵正是借助Info命令来获取所有复制该主数据库的从数据库信息的。启动后,哨兵向主数据库发送INFO命令,通过解析返回结果来得知从数据库列表,而后对每个从数据库同样建立两个连接,两个连接的作用和前文介绍的与主数据库建立的两个连接完全一致。在此之后,哨兵会每10秒定时向已知的所有主从数据库发送
INFO
命令来获取信息更新并进行相应操作,比如- 对新增的从数据库建立连接并加入监控列表
- 对主从数据库的角色变化(由故障恢复操作引起)进行信息更新等
-
接下来哨兵向主从数据库的
sentinel:he11o
频道发送信息来与同样监控该数据库的哨兵分享自己的信息。- 发送的消息内容为:
<哨兵的地址>,<哨兵的端口>,<哨兵的运行ID>,<哨兵的配置版本>,<主数据库的名字>,
<主数据库的地址>,<主数据库的端口>,<主数据库的配置版本>
可以看到消息包括的哨兵的基本信息,以及其监控的主数据库的信息
- 哨兵会订阅每个其监控的数据库的
sentinel:he11o
频道,所以当其他哨兵收到消息后,会判断发消息的哨兵是不是新发现的哨兵。如果是则将其加入已发现的哨兵列表中并创建一个到其的连接(与数据库不同,哨兵与哨兵之间只会创建一条连接用来发送命令,而不需要创建另外一条连接来订阅频道,因为哨兵只需要订阅数据库的频道即可实现自动发现其他哨兵)。同时哨兵会判断信息中主数据库的配置版本,如果该版本比当前记录的主数据库的版本高,则更新主数据库的数据。
- 发送的消息内容为:
-
实现了自动发现从数据库和其他哨兵节点后,哨兵要做的就是定时监控这些节点有没有停止服务。这是通过每隔一定时间向这些节点发送PING命令实现的。
时间间隔与down-after-milliseconds选项有关- 当down-after-milliseconds的值小于1秒时,哨兵会每隔down-after-milliseconds指定的时间发送一次PING命令,
- 当down-after-milliseconds的值大于1秒时,哨兵会每隔1秒发送一次PING命令
当超过down-after-milliseconds选项指定时间后,如果被PING的数据库或节点仍然未进行回复,则哨兵认为其主观下线(subjectively down)。主观下线表示从当前的哨兵进程看来,该节点已经下线。
如果该节点是主数据库,则哨兵会进一步判断是否需要对其进行故障恢复:
哨兵发送
SENTINEL is-master-down-by-addr
命令询问其他哨兵节点以了解他们是否也认为该主数据库主观下线,如果达到指定数量时,哨兵会认为其客观下线(objectively down),并选举领头的哨兵节点对主从系统发起故障恢复。
这个指定数量即为前文介绍的quorum
参数,只有当至少quorum个Sentinel节点(包括当前节点)认为该主数据库主观下线时,当前哨兵节点才会认为该主数据库客观下线。
四、选举领头哨兵步骤
虽然当前哨兵节点发现了主数据库客观下线,需要故障恢复,但是故障恢复需要由领头的哨兵来完成,这样可以保证同一时间只有一个哨兵节点来执行故障恢复。
选举领头哨兵的过程使用了Raft算法,具体过程如下。
- 发现主数据库客观下线的哨兵节点(下面称作A)向每个哨兵节点发送命令,要求对方选自己成为领头哨兵.
- 如果目标哨兵节点没有选过其他人,则会同意将A设置成领头哨兵.
- 如果A发现有超过半数且超过
quorum
参数值的哨兵节点同意选自己成为领头哨兵,则A成功称为领头哨兵。 - 当有多个哨兵节点同时参选领头哨兵,则会出现没有任何节点当选的可能。此时每个参选节点将等待一个随机时间重新发起参选请求,进行下一轮选举,直到选举成功。
具体过程可以参考Raft算法的过程http://raftconsensus.github.io/。因为要成为领头哨兵必须有超过半数的哨兵节点支持,所以每次选举最多只会选出一个领头哨兵。
选出领头哨兵后,领头哨兵将会开始对主数据库进行故障恢复。
五、故障恢复
- 首先领头哨兵将从停止服务的主数据库的从数据库中挑选一个来充当新的主数据库。
挑选的依据如下。- 所有在线的从数据库中,选择优先级最高的从数据库。优先级可以通过
slave-priority
选项来设置。 - 如果有多个最高优先级的从数据库,则复制的命令偏移量越大(即复制越完整)越优先。
命令偏移量可参考redis的增量复制
- 如果以上条件都一样,则选择
运行ID
较小的从数据库。
- 所有在线的从数据库中,选择优先级最高的从数据库。优先级可以通过
- 选出一个从数据库后,领头哨兵将向从数据库发送
SLAVEOF NO ONE
命令使其升格为主数据库。领头哨兵向其他从数据库发送SLAVEOF
命令来使其成为新主数据库的从数据库。 - 最后一步则是更新内部的记录,将已经停止服务的旧的主数据库更新为新的主数据库的从数据库,使得当其恢复服务时自动以从数据库的身份继续服务。