当主库宕机了,客户端如何进行写操作?
此时就需要哨兵来帮忙,将一个从库升级为主库。
哨兵主要负责的就是三个任务:监控、选择主库和通知。
监控
监控是指哨兵进程在运行时,周期性地给所有的主从库发送 PING 命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的 PING 命令,哨兵就会把它标记为“下线状态”;
同样,如果主库也没有在规定时间内响应哨兵的 PING 命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。
下线判断分为“主观下线”和“客观下线”两种。
选择主库
主库挂了以后,哨兵就需要从很多个从库里,按照一定的规则选择一个从库实例,把它作为新的主库。
选择新主库的过程称为“筛选 + 打分”。
通知
哨兵会把新主库的连接信息发给其他从库,让它们执行 replicaof 命令,和新主库建立连接,并进行数据复制。
同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。
主观下线
哨兵进程会使用 PING 命令检测它自己和主、从库的网络连接情况,用来判断实例的状态。
如果哨兵发现从库对 PING 命令的响应超时了。由于从库的下线影响一般不太大,可直接标记为“主观下线”。
而主库则不可这么随便,一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。为了避免哨兵误判(一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下)。
通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
客观下线
在哨兵集群的情况下,只有大多数的哨兵实例,都判断主库已经“主观下线”了,主库才会被标记为“客观下线”。判断原则就是:少数服从多数。
当有 N 个哨兵实例时,最好要有 N/2 + 1 个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。
筛选
检查从库的当前在线状态,判断它之前的网络连接状态。(down-after-milliseconds)
如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络联系上,我们就可以认为主从节点断连了。如果发生断连的次数超过了 n 次,就说明这个从库的网络状况不好,不适合作为新主库。
要保证所有哨兵实例的配置是一致的,尤其是主观下线的判断值 down-after-milliseconds。否则可能导致集群服务不稳定。
打分
1,优先级最高的从库得分高(slave-priority 配置项)
2,和旧主库同步程度最接近的从库得分高(repl_backlog_buffer:详见上篇文章)
3,ID 号小的从库得分高
哨兵见如何互相通信?
通过 pub/sub (发布/订阅)机制,哨兵只要和主库建立起了连接,每个哨兵都把自己的信息发送给主库,然后从主库订阅其他哨兵的消息,这样就可以互相知道其他哨兵的地址了。
哨兵是如何知道从库的 IP 地址和端口的呢?
这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。
通过 pub/sub 机制,哨兵之间可以组成集群,同时,哨兵又通过 INFO 命令,获得了从库连接信息,也能和从库建立连接,并进行监控了
主从切换后,哨兵是如何告知客户端新的主库
通过 pub/sub (发布/订阅)机制,客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件。
主库故障以后,哨兵集群有多个实例,那怎么确定由哪个哨兵来进行实际的主从切换呢?
1,任何一个实例只要自身判断主库“主观下线”后,就会给其他实例发送 is-master-down-by-addr 命令。接着,其他实例会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。
2,一个哨兵获得了仲裁所需的赞成票数后,就可以标记主库为“客观下线”。
3,此时,这个哨兵就可以再给其他哨兵发送命令,表明希望由自己来执行主从切换,并让所有其他哨兵进行投票。这个投票过程称为“Leader 选举”。
如果哨兵集群只有 2 个实例,此时,一个哨兵要想成为 Leader,必须获得 2 票,而不是 1 票。所以,如果有个哨兵挂掉了,那么,此时的集群是无法进行主从库切换的。因此,通常我们至少会配置 3 个哨兵实例。
哨兵在操作主从切换的过程中,客户端能否正常地进行请求操作?
如果客户端使用了读写分离,那么读请求可以在从库上正常执行,不会受到影响。但是由于此时主库已经挂了,而且哨兵还没有选出新的主库,所以在这期间写请求会失败,失败持续的时间 = 哨兵切换主从的时间 + 客户端感知到新主库 的时间。
如果不想让业务感知到异常,客户端只能把写失败的请求先缓存起来或写入消息队列中间件中,等哨兵切换完主从后,再把这些写请求发给新的主库,但这种场景只适合对写入请求返回值不敏感的业务,而且还需要业务层做适配,另外主从切换时间过长,也会导致客户端或消息队列中间件缓存写请求过多,切换完成之后重放这些请求的时间变长。
哨兵检测主库多久没有响应就提升从库为新的主库,这个时间是可以配置的(down-after-milliseconds参数)。配置的时间越短,哨兵越敏感,哨兵集群认为主库在短时间内连不上就会发起主从切换,这种配置很可能因为网络拥塞但主库正常而发生不必要的切换,当然,当主库真正故障时,因为切换得及时,对业务的影响最小。如果配置的时间比较长,哨兵越保守,这种情况可以减少哨兵误判的概率,但是主库故障发生时,业务写失败的时间也会比较久,缓存写请求数据量越多。
哨兵提升一个从库为新主库后,客户端因为某些原因错过了哨兵的通知怎么办?
哨兵提升一个从库为新主库后,哨兵会把新主库的地址写入自己实例的pubsub(switch-master)中。客户端需要订阅这个pubsub,当这个pubsub有数据时,客户端就能感知到主库发生变更,同时可以拿到最新的主库地址,然后把写请求写到这个新主库即可,这种机制属于哨兵主动通知客户端。
如果客户端因为某些原因错过了哨兵的通知,或者哨兵通知后客户端处理失败了,安全起见,客户端也需要支持主动去获取最新主从的地址进行访问。
所以,客户端需要访问主从库时,不能直接写死主从库的地址了,而是需要从哨兵集群中获取最新的地址(sentinel get-master-addr-by-name命令),这样当实例异常时,哨兵切换后或者客户端断开重连,都可以从哨兵集群中拿到最新的实例地址。
即为推拉结合。
redis主从切换维持数据最终一致性吗?(数据是否会丢失?)
redis不是最终一致性,leader未同步数据给flow节点时挂了发生主从切换,未同步的数据就丢失了。
而像ZooKeeper这类ZAB协议,是要Leader同步超过半数Flow节点,才告知客户的写操作成功,如果Leader挂了重新选出的Leader也有最新的数据。
哨兵实例是不是越多越好?
并不是,哨兵在判定“主观下线”和选举“哨兵领导者”时,都需要和其他节点进行通信,交换信息,哨兵实例越多,通信的次数也就越多,而且部署多个哨兵时,会分布在不同机器上,节点越多带来的机器故障风险也会越大,这些问题都会影响到哨兵的通信和选举,出问题时也就意味着选举时间会变长,切换主从的时间变久。
小总结
哨兵集群的构建主要通过pub/sub机制来组成。
哨兵集群基于INFO命令和所有从库建立连接。
哨兵和从库之间可以通过pub/sub机制完成消息的订阅和事件通知。
哨兵认定主库客观下线,需要有超过quorum值数量的哨兵认定主库为主观下线。认定了主库为客观下线的哨兵有资格开始成为领导者哨兵的候选人,并发起投票。
只有候选人才能够投自己一票,其他的哨兵(未认定主库为客观下线的)只能够投给第一个接收到的投票请求。并且只能投赞成票一次。后者都是反对票。
热爱技术才能学好技术
每天进步一点点
领取专属 10元无门槛券
私享最新 技术干货