主库故障了从库该怎么办,数据还能保持一致吗,Redis 还能正常提供服务吗?
先谈哨兵机制
哨兵主要负责的就是三个任务:监控、选主(选择主库)和通知。
哨兵咋监控的呢?
哨兵进程会使用 PING 命令检测它自己和主、从库的网络连接情况,用来判断实例的状态。如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。
通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
只有大多数的哨兵实例,都判断主库已经“主观下线”了,主库才会被标记为“客观下线”
如何选定新主库呢?
哨兵选择新主库的过程称为“筛选 + 打分”
筛选:检查从库的当前在线状态,还要判断它之前的网络连接状态。如果从库总是和主库断连,而且断连次数超出了一定的阈值,我们就有理由相信,这个从库的网络状况并不是太好,就可以把这个从库筛掉了。
打分:依据从库优先级、从库复制进度以及从库 ID 号。
第一轮:优先级最高的从库得分高。
第二轮:和旧主库同步程度最接近的从库得分高。
第三轮:ID 号小的从库得分高。
哨兵集群是怎么建立的?
- 哨兵彼此之间建立连接形成集群
基于 pub/sub 机制(发布 / 订阅机制)的哨兵集群组成
哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和端口)。同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息。当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的 IP 地址和端口。只有订阅了同一个频道的应用,才能通过发布的消息进行信息交换。
- 哨兵和从库建立连接
基于 INFO 命令的从库列表
哨兵给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。
- 哨兵和客户端间的信息同步
基于 pub/sub 机制的客户端事件通知
哨兵就是一个运行在特定模式下的 Redis 实例,只不过它并不服务请求操作,只是完成监控、选主和通知的任务。所以,每个哨兵实例也提供 pub/sub 机制,客户端可以从哨兵订阅消息。哨兵提供的消息订阅频道有很多,不同频道包含了主从库切换过程中的不同关键事件。
哨兵集群由哪个实例来执行主从切换呢?
哨兵集群在判断了主库“客观下线”后,经过投票仲裁,选举一个 Leader 出来,由它负责实际的主从切换,即由它来完成新主库的选择以及通知从库与客户端。
这个选举的过程,就是「分布式共识」,即多个哨兵通过「投票」选举出一个都认可的实例当领导者,由这个领导者发起切换,这个选举使用的算法是 Raft 算法
严格来说,Raft 算法的核心流程是这样的:
- 集群正常情况下,Leader 会持续给 Follower 发心跳消息,维护 Leader 地位
- 如果 Follower 一段时间内收不到 Leader 心跳消息,则变为 Candidate 发起选举
- Candidate 先给自己投一票,然后向其它节点发送投票请求
- Candidate 收到超过半数确认票,则提升为新的 Leader,新 Leader 给其它 Follower 发心跳消息,维护新的 Leader 地位
- Candidate 投票期间,收到了 Leader 心跳消息,则自动变为 Follower
- 投票结束后,没有超过半数确认票的实例,选举失败,会再次发起选举
但哨兵的选举没有按照严格按照 Raft 实现,因为多个哨兵之间是「对等」关系,没有 Leader 和 Follower 角色,只有当 Redis 实例发生故障时,哨兵才选举领导者进行切换,选举 Leader 的过程是按照 Raft 算法步骤 3-6 实现的。raft有任期的概念,网络恢复后旧的master发现有任期比他大的master,会自动变成follower。