Redis(一)哨兵模式原理

Redis哨兵模式原理

Redis哨兵机制是保证了Redis服务的高可用,在master故障的情况下,在slave中选取一台作为新的master,并将新的master通知其他哨兵和客户端。哨兵机制主要执行以下三个任务

  • 监控master和slave的状态。
  • 故障转移,主从切换。监控到master宕机了,那么在slave中选举一个新的master,并将其他的slave指向新的master
  • 通知客户端和或者其他程序使用新的master

工作原理

在哨兵集群启动的时候,每个节点都会启动三个定时任务。

  • 第一个定时任务是:每个哨兵都会定时向其他哨兵或者master、slave发送ping命令。用于心跳检测
  • 第二个定时任务是:每个哨兵都会定时向master的_sentinel_:hello这个channel发送自己掌握的最新的集群信息和自身的一些信息,这个是利用Redis的pub/sub消息订阅功能实现的,每个哨兵都会订阅这个channel,所以每个哨兵都能接收到其他哨兵所掌握的信息。如果哨兵正在接收到的master配置比上次接收到master配置要旧,则哨兵会马上更新master的配置。
  • 第三个定时任务是:每个哨兵都会定时向master和slave返送info命令,获取最新的集群拓扑。

如何判断master宕机

主观下线(SDOWN)

根据面所说的第一个定时任务,哨兵会定时向master 发送ping命令,如果在一定的时间(默认为30s,配置is-master-down-after-milliseconds修改默认值)没有得master的响应,则哨兵就认为该master主观下线了,将该master的flag状态记为SRI_S_DOWN,并且没秒向其他哨兵节点发送is-master-down-by-addr <ip> <port>命令确定master的真实状态。

哨兵向master发送ping命令,在规定的时间内如果返回了除以下消息的任意消息或者无响应,则视为主观下线

  • PONG
  • LOADING
  • MASTERDOWN

客观下线(ODOWN)

所谓的客观下线指的是,哨兵集群中超过一定数量的哨兵认为该master已经主观下线了,则认为该master是客观下线了,集群中认为master主观下线的哨兵数量是通过quorum这个参数指定的,例如哨兵集群中有5个节点,quorum配置为3,那么如果集群中3个哨兵认为该master已经主观下线了,那么就说明该master已经客观下线了。

如果哨兵集群任务该master客观下线,那么就需要其他slave节点中的一个slave节点变成master,slave变为master这个任务并不是整个哨兵集群做的,而是哨兵集群选举一个leader哨兵来干这个事情,那么这个leader是怎么选取出来的呢。

哨兵leader选举

选举原理

  • 每个认为master主观下线的哨兵都有公平选举的机会
  • 同一轮选举中,每个哨兵只能投一次票,如果已经投一次票了,就不能再次进行投票了
  • 同意票是先到先得
  • 如果参加竞选的哨兵向其他哨兵发送is-master-down-by-add ip port current_epoch runid命令中,如果runid不是*,意思就是让其他哨兵选他为leader
  • 如果一个哨兵票数超过半数,则成为leader
  • 如果选取了多个leader,则等待一段时间重新选举

选举过程

  • 每个认为master主观下线的哨兵会向每个节点发送is-master-down-by-addr ip port current_epoch runid命令,要求其他节点同意自己成为leader
  • 每个哨兵接收到is-master-down-by-addr ip port current_epoch runid 命令后,如果还没有投过票,则同意,否则拒绝。
  • 如果其中某些节点的票数已过半,则成为leader、
  • 如果这个过程中出现了多个leader,则等待会重新选举。

故障转移

首先要能理解SDOWN和ODOWN这两个词的含义,上面已经详细介绍了它们俩。在此再提一下:
SDOWN:subjectively down,直接翻译的为"主观"失效,即当前sentinel实例认为某个redis服务为"不可用"状态.
ODOWN:objectively down,直接翻译为"客观"失效,即多个sentinel实例都认为master处于"SDOWN"状态,那么此时master将处于ODOWN,ODOWN可以简单理解为master已经被集群确定
为"不可用",将会开启failover.
 
SDOWN适合于master和slave,但是ODOWN只会使用于master;当slave失效超过"down-after-milliseconds"后,那么所有sentinel实例都会将其标记为"SDOWN"1) SDOWN与ODOWN转换过程:
每个sentinel实例在启动后,都会和已知的slaves/master以及其他sentinels建立TCP连接,并周期性发送PING(默认为1秒)
在交互中,如果redis-server无法在"down-after-milliseconds"时间内响应或者响应错误信息,都会被认为此redis-server处于SDOWN状态。
如果SDOWN的server为master,那么此时sentinel实例将会向其他sentinel间歇性(一秒)发送"is-master-down-by-addr <ip> <port>"指令并获取响应信息,如果足够多的
sentinel实例检测到master处于SDOWN,那么此时当前sentinel实例标记master为ODOWN...其他sentinel实例做同样的交互操作。
配置项"sentinel monitor <mastername> <masterip> <masterport> <quorum>",如果检测到master处于SDOWN状态的slave个数达到<quorum>,那么此时此sentinel实例将会认为
master处于ODOWN。每个sentinel实例将会间歇性(10)向master和slaves发送"INFO"指令,如果master失效且没有新master选出时,每1秒发送一次"INFO";"INFO"的主要目的就是
获取并确认当前集群环境中slaves和master的存活情况。
 
经过上述过程后,所有的sentinel对master失效达成一致后,开始failover.
 
2) Sentinel与slaves"自动发现"机制:
在sentinel的配置文件中(local-sentinel.conf),都指定了port,此port就是sentinel实例侦听其他sentinel实例建立链接的端口.在集群稳定后,最终会每个sentinel实例之间都
会建立一个tcp链接,此链接中发送"PING"以及类似于"is-master-down-by-addr"指令集,可用用来检测其他sentinel实例的有效性以及"ODOWN""failover"过程中信息的交互.
 
在sentinel之间建立连接之前,sentinel将会尽力和配置文件中指定的master建立连接.sentinel与master的连接中的通信主要是基于pub/sub来发布和接收信息,发布的信息内容包
括当前sentinel实例的侦听端口:
+sentinel sentinel 127.0.0.1:26579 127.0.0.1 26579 .... 
 
发布的主题名称为"__sentinel__:hello";同时sentinel实例也是"订阅"此主题,以获得其他sentinel实例的信息.由此可见,环境首次构建时,在默认master存活的情况下,所有的
sentinel实例可以通过pub/sub即可获得所有的sentinel信息,此后每个sentinel实例即可以根据+sentinel信息中的"ip+port"和其他sentinel逐个建立tcp连接即可.不过需要提醒
的是,每个sentinel实例均会间歇性(5)"__sentinel__:hello"主题中发布自己的ip+port,目的就是让后续加入集群的sentinel实例也能或得到自己的信息。
根据上文,我们知道在master有效的情况下,即可通过"INFO"指令获得当前master中已有的slave列表;此后任何slave加入集群,master都会向"主题中"发布"+slave 127.0.0.1:6579 ..",
那么所有的sentinel也将立即获得slave信息,并和slave建立链接并通过PING检测其存活性.
 
补充一下,每个sentinel实例都会保存其他sentinel实例的列表以及现存的master/slaves列表,各自的列表中不会有重复的信息(不可能出现多个tcp连接),对于sentinel将使用ip+port
做唯一性标记,
对于master/slaver将使用runid做唯一性标记,其中redis-server的runid在每次启动时都不同.
 
3) Leader选举:
其实在sentinels故障转移中,仍然需要一个"Leader"来调度整个过程:master的选举以及slave的重配置和同步。当集群中有多个sentinel实例时,如何选举其中一个sentinel为leader呢?
 
在配置文件中"can-failover""quorum"参数,以及"is-master-down-by-addr"指令配合来完成整个过程。
A) "can-failover"用来表明当前sentinel是否可以参与"failover"过程,如果为"YES"则表明它将有能力参与"Leader"的选举,否则它将作为"Observer",observer参与leader选举投票但
不能被选举;
B) "quorum"不仅用来控制master ODOWN状态确认,同时还用来选举leader时最小"赞同票"数;
C) "is-master-down-by-addr",它可以用来检测"ip + port"的master是否已经处于SDOWN状态,不过此指令不仅能够获得master是否处于SDOWN,同时它还额外的返回当前sentinel
本地"投票选举"的Leader信息(runid);
 
每个sentinel实例都持有其他的sentinels信息,在Leader选举过程中(当为leader的sentinel实例失效时,有可能master server并没失效,注意分开理解),sentinel实例将从所有的
sentinels集合中去除"can-failover = no"和状态为SDOWN的sentinels,在剩余的sentinels列表中按照runid按照"字典"顺序排序后,取出runid最小的sentinel实例,并将它"投票选举"
为Leader,并在其他sentinel发送的"is-master-down-by-addr"指令时将推选的runid追加到响应中。每个sentinel实例都会检测"is-master-down-by-addr"的响应结果,如果"投票选举"的
leader为自己,且状态正常的sentinels实例中,"赞同者"的自己的sentinel个数不小于(>=) 50% + 1,且不小与<quorum>,那么此sentinel就会认为选举成功且leader为自己。
在sentinel.conf文件中,我们期望有足够多的sentinel实例配置"can-failover yes",这样能够确保当leader失效时,能够选举某个sentinel为leader,以便进行failover。如果leader无法产生,
比如较少的sentinels实例有效,那么failover过程将无法继续.
 
4) failover过程:
在Leader触发failover之前,首先wait数秒(随即0~5),以便让其他sentinel实例准备和调整(有可能多个leader??),如果一切正常,那么leader就需要开始将一个salve提升为master,此slave
必须为状态良好(不能处于SDOWN/ODOWN状态)且权重值最低(redis.conf中)的,当master身份被确认后,开始failover
A)"+failover-triggered": Leader开始进行failover,此后紧跟着"+failover-state-wait-start",wait数秒。
B)"+failover-state-select-slave": Leader开始查找合适的slave
C)"+selected-slave": 已经找到合适的slave
D) "+failover-state-sen-slaveof-noone": Leader向slave发送"slaveof no one"指令,此时slave已经完成角色转换,此slave即为master
E) "+failover-state-wait-promotition": 等待其他sentinel确认slave
F)"+promoted-slave":确认成功
G)"+failover-state-reconf-slaves": 开始对slaves进行reconfig操作。
H)"+slave-reconf-sent":向指定的slave发送"slaveof"指令,告知此slave跟随新的master
I)"+slave-reconf-inprog": 此slave正在执行slaveof + SYNC过程,如过slave收到"+slave-reconf-sent"之后将会执行slaveof操作。
J)"+slave-reconf-done": 此slave同步完成,此后leader可以继续下一个slave的reconfig操作。循环G)
K)"+failover-end": 故障转移结束
L)"+switch-master":故障转移成功后,各个sentinel实例开始监控新的master。

配置版本号

为什么要先获得大多数sentinel的认可时才能真正去执行failover呢?
当一个sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号,当failover执行结束以后,这个版本号将会被用于最新的配置。因为大多数sentinel都已经知道该版本号已经被要执行failover的sentinel拿走了,所以其他的sentinel都不能再去使用这个版本号。这意味着,每次failover都会附带有一个独一无二的版本号。我们将会看到这样做的重要性。而且,sentinel集群都遵守一个规则:如果sentinel A推荐sentinel B去执行failover,B会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是通过failover-timeout配置项去配置的。从这个规则可以看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel如果失败了,另外一个将会在一定时间内进行重新进行failover,以此类推。
redis sentinel保证了活跃性:如果大多数sentinel能够互相通信,最终将会有一个被授权去进行failover.
redis sentinel也保证了安全性:每个试图去failover同一个master的sentinel都会得到一个独一无二的版本号。

配置传播

一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。
一个faiover要想被成功实行,sentinel必须能够向选为master的slave发送REPLICAOF NO ONE(提升为主服务器)命令,然后能够通过INFO命令看到master的配置信息。
当将一个slave选举为master并发送REPLICAOF NO ONE后,即使其它的slave还没针对新master重新配置自己,failover也被认为是成功了的,然后所有sentinels将会发布新的配置信息。
新配在集群中相互传播的方式,就是为什么我们需要当一个sentinel进行failover时必须被授权一个版本号的原因。
每个sentinel使用##发布/订阅##的方式持续地传播master的配置版本信息,配置传播的##发布/订阅##管道是:sentinel:hello。
因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。

举个例子:
假设有一个名为mymaster的地址为192.168.10.202:6379。一开始,集群中所有的sentinel都知道这个地址,于是为mymaster的配置打上版本号1。一段时候后mymaster死了,有一个sentinel被授权用版本号2对其进行failover。如果failover成功了,假设地址改为了192.168.10.202:9000,此时配置的版本号为2,进行failover的sentinel会将新配置广播给其他的sentinel,由于其他sentinel维护的版本号为1,发现新配置的版本号为2时,版本号变大了,说明配置更新了,于是就会采用最新的版本号为2的配置。
这意味着sentinel集群保证了第二种活跃性:一个能够互相通信的sentinel集群最终会采用版本号最高且相同的配置。

哨兵和副本的自动发现

哨兵与其他哨兵保持连接,以便于相互检查是否存活,并进行交换信息。然而你不需要在每个运行中的哨兵实例上配置哨兵列表,因为哨兵使用的是Redis的Pub/Sub消息发布订阅,为得是发现正在监控相同的master和副本的哨兵节点。这种特性是通过向名字为__sentinel__:hello的channel中发送hello message消息实现的。

类似的你也不需要配置master的副本列表,因为哨兵会通过查询redis自动发现副本列表。

  • 每个哨兵每2秒向正在受监控master和replica的pub/sub 的channel发送message消息。宣布其ip、端口、runid的存在。

  • 每个哨兵都订阅了每个master和replica的__sentinel__:hello的channel通道来发现未知的哨兵,当有新的哨兵来的时候,该哨兵会成为该master的哨兵。

  • hello message包含当前master的全部配置,如果哨兵正在接收的master配置比上一次接收到的master配置新,则立即更新最新的配置

  • 当向master添加新的哨兵的时候,哨兵总是检查是否已经存在相同runid的哨兵,如果存在则移除,将新的哨兵添加进来。

没有故障转移的情况下哨兵实例的重新配置

即使在没有故障转移的情况下,哨兵也总是尝试设置当前配置到被监控的实例上。具体如下:

  • 连接到错误的master的replica会重新配置到正常的master上。
  • 声明为master的replica会配置为replica,然后与master进行复制。

副本选择和优先级

当前哨兵已经准备开始故障转移,由于master已经处于ODOWN状态,并且当前哨兵已经从大多数哨兵那里获取到了故障转移的权限,因此要选择一个合适的replica来成为master。

选择副本成为master,需要根据以下信息来确定

  • 与master断开连接的时间
  • 副本优先级
  • 副本处理的offset(offset就是replica同步master数据偏移量,越大说明replica与master的数据越接近)
  • runid

一个replica被发现超时时间超过了master配置的超时间的十倍以上,该replica被认为不可靠的节点,将会被忽略。通过info命令可以查看replica与master断开连接的时间,计算公式为:

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

replica成为master的可能,是按照一下顺序进行选举的

  1. Redis的配置文件redis.confreplica-prioriy的配置值,值越小优先级越高,优先级最高的选为master
  2. 如果优先级相同,检查replica的offset,哪个replica复制master的数据越多,就选择哪个replica作为master
  3. 如果优先级和offset相同,则选择最小的runid成为master

如果replicat的replica-prioriy的配置值为0,则该replica永远不会成为master

算法和内部原理

Quorum

每一个受哨兵监控的master都与quorum配置相关联。它是一个边界,如果哨兵集群中认为master已经主观下线的哨兵数量超过了这个边界,则标记该master为客观下线。

然而触发故障转移以后,要想真正的实现故障转移,大多数哨兵需要授权一个哨兵去处理故障转移。触发故障转移需要满足一下条件

  • 大多数哨兵认为master处于主观下线
  • master处于客观下线状态
  • 大多数哨兵授权一个哨兵成为故障转移的执行者。

其实看起来很繁琐,但是其很简单。例如如果哨兵集群中有5个节点,quorum配置为2,则只要有2个哨兵认为master主观下线,则可以标记master为客观下线。但是成为故障转移的执行者需要有至少3个节点的授权。

这就意味quorum参数配置有两种形式,一种是小于哨兵集群节点的大多数,一种是小于哨兵集群节点的大多数

  • 如果quorum配置小于大多数,那么只要有大多数的哨兵授权就可以执行故障转移
  • 如果quorum配置大于大多数,那么需要quorum个哨兵都授权才能进行故障转移

configuration epoch

哨兵会对一套redis master+slave进行监控,有相应的监控的配置。执行切换的那个哨兵,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的。如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号。

哨兵配置

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

哨兵脑裂

可能由于网络环境的原因,哨兵认为master已经宕机,然后进行新的master切换,切换成功以后,可能还没来得及通知客户端新的master,所以客户端依然请求旧的master,这样数据就写入了旧的master,由于旧的master恢复过来会成为新的replica的,会清空自己的数据,重新去master复制数据,这样客户端写入的部分数据就会丢失。

Sentinel命令

  • SENTINEL CONFIG GET<name> :获取哨兵当前的全局配置参数,name可以是通配符。

  • SENTINEL CONFIG SET<name> <value> :设置哨兵的全局参数

  • SENTINEL CKQUORUM<master name>:检查master是否达到了哨兵配置的quorum人数,以及授权故障转移所需的大多数人数。此命令用于监控系统以及检查哨兵的部署是否正常。

  • SENTINEL FLUSHCONFIG:强制将哨兵的最新配置刷新到磁盘上。默认情况下每次哨兵状态变化的时候会自动将最新配置和状态刷新到磁盘中,但是有时候可能因为磁盘异常、操作错误等原因导致刷盘失败,强制刷盘是为了防止哨兵配置和状态数据的丢失。

  • SENTINEL FLUSHCONFIG:强制故障转移。不需要经过其他哨兵节点的同意。

  • SENTINEL GET-MASTER-ADDR-BY-NAME<master name>:返回具有该名称的 master 的 ip 和端口号。如果此主服务器的故障转移正在进行或成功终止,它会返回提升的副本的地址和端口

  • SENTINEL MASTER<master name>:显示指定主机的状态和信息

  • SENTINEL MASTERS:显示受监视的主机及其状态的列表

  • SENTINEL MYID ( >= 6.2): 返回 Sentinel 实例的 ID

  • SENTINEL REMOVE:停止 Sentinel 的监控

  • SENTINEL REPLICAS<master name> ( >= 5.0) :显示此主服务器的副本列表及其状态

  • SENTINEL SENTINELS<master name>:显示此 master 的哨兵实例列表及其状态

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值