Redis 哨兵集群实现高可用

哨兵的介绍

sentinel,中文名是哨兵。哨兵是 redis 集群机构中很是重要的一个组件,主要有如下功能:node

集群监控:负责监控 redis master 和 slave 进程是否正常工做。
消息通知:若是某个 redis 实例有故障,那么哨兵负责发送消息做为报警通知给管理员。
故障转移:若是 master node 挂掉了,会自动转移到 slave node 上。
配置中心:若是故障转移发生了,通知 client 客户端新的 master 地址。
哨兵用于实现 redis 集群的高可用,自己也是分布式的,做为一个哨兵集群去运行,互相协同工做。redis

故障转移时,判断一个 master node 是否宕机了,须要大部分的哨兵都赞成才行,涉及到了分布式选举的问题。
即便部分哨兵节点挂掉了,哨兵集群仍是能正常工做的,由于若是一个做为高可用机制重要组成部分的故障转移系统自己是单点的,那就很坑爹了。算法

哨兵的核心知识

哨兵至少须要 3 个实例,来保证本身的健壮性。
哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
对于哨兵 + redis 主从这种复杂的部署架构,尽可能在测试环境和生产环境,都进行充足的测试和演练。
哨兵集群必须部署 2 个以上节点,若是哨兵集群仅仅部署了 2 个哨兵实例,quorum = 1。sql

+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+网络

配置 quorum=1,若是 master 宕机, s1 和 s2 中只要有 1 个哨兵认为 master 宕机了,就能够进行切换,同时 s1 和 s2 会选举出一个哨兵来执行故障转移。可是同时这个时候,须要 majority,也就是大多数哨兵都是运行的。架构

2 个哨兵,majority=2
3 个哨兵,majority=2
4 个哨兵,majority=2
5 个哨兵,majority=3
...并发

若是此时仅仅是 M1 进程宕机了,哨兵 s1 正常运行,那么故障转移是 OK 的。可是若是是整个 M1 和 S1 运行的机器宕机了,那么哨兵只有 1 个,此时就没有 majority 来容许执行故障转移,虽然另一台机器上还有一个 R1,可是故障转移不会执行。异步

经典的 3 节点哨兵集群是这样的:分布式

clipboard.png

配置 quorum=2,若是 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 能够一致认为 master 宕机了,而后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,因此还剩下的 2 个哨兵运行着,就能够容许执行故障转移。高并发

redis 哨兵主备切换的数据丢失问题

两种状况和致使数据丢失
主备切换的过程,可能会致使数据丢失:

异步复制致使的数据丢失
由于 master->slave 的复制是异步的,因此可能有部分数据还没复制到 slave,master 就宕机了,此时这部分数据就丢失了。

脑裂致使的数据丢失
脑裂,也就是说,某个 master 所在机器忽然脱离了正常的网络,跟其余 slave 机器不能链接,可是实际上 master 还运行着。此时哨兵可能就会认为 master 宕机了,而后开启选举,将其余 slave 切换成了 master。这个时候,集群里就会有两个 master ,也就是所谓的脑裂。

此时虽然某个 slave 被切换成了 master,可是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。所以旧 master 再次恢复的时候,会被做为一个 slave 挂到新的 master 上去,本身的数据会清空,从新重新的 master 复制数据。而新的 master 并无后来 client 写入的数据,所以,这部分数据也就丢失了。

数据丢失问题的解决方案

进行以下配置:

min-slaves-to-write 1
min-slaves-max-lag 10

表示,要求至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒。

若是说一旦全部的 slave,数据复制和同步的延迟都超过了 10 秒钟,那么这个时候,master 就不会再接收任何请求了。

减小异步复制数据的丢失

有了 min-slaves-max-lag 这个配置,就能够确保说,一旦 slave 复制数据和 ack 延时太长,就认为可能 master 宕机后损失的数据太多了,那么就拒绝写请求,这样能够把 master 宕机时因为部分数据未同步到 slave 致使的数据丢失下降的可控范围内。

减小脑裂的数据丢失

若是一个 master 出现了脑裂,跟其余 slave 丢了链接,那么上面两个配置能够确保说,若是不能继续给指定数量的 slave 发送数据,并且 slave 超过 10 秒没有给本身 ack 消息,那么就直接拒绝客户端的写请求。所以在脑裂场景下,最多就丢失 10 秒的数据。

sdown 和 odown 转换机制

sdown 是主观宕机,就一个哨兵若是本身以为一个 master 宕机了,那么就是主观宕机
odown 是客观宕机,若是 quorum 数量的哨兵都以为一个 master 宕机了,那么就是客观宕机
sdown 达成的条件很简单,若是一个哨兵 ping 一个 master,超过了 is-master-down-after-milliseconds 指定的毫秒数以后,就主观认为 master 宕机了;若是一个哨兵在指定时间内,收到了 quorum 数量的其它哨兵也认为那个 master 是 sdown 的,那么就认为是 odown 了。

哨兵集群的自动发现机制

哨兵互相之间的发现,是经过 redis 的 pub/sub 系统实现的,每一个哨兵都会往 __sentinel__:hello 这个 channel 里发送一个消息,这时候全部其余哨兵均可以消费到这个消息,并感知到其余的哨兵的存在。

每隔两秒钟,每一个哨兵都会往本身监控的某个 master+slaves 对应的 __sentinel__:hello channel 里发送一个消息,内容是本身的 host、ip 和 runid 还有对这个 master 的监控配置。

每一个哨兵也会去监听本身监控的每一个 master+slaves 对应的 __sentinel__:hello channel,而后去感知到一样在监听这个 master+slaves 的其余哨兵的存在。

每一个哨兵还会跟其余哨兵交换对 master 的监控配置,互相进行监控配置的同步。

slave 配置的自动纠正

哨兵会负责自动纠正 slave 的一些配置,好比 slave 若是要成为潜在的 master 候选人,哨兵会确保 slave 复制现有 master 的数据;若是 slave 链接到了一个错误的 master 上,好比故障转移以后,那么哨兵会确保它们链接到正确的 master 上。

slave->master 选举算法

若是一个 master 被认为 odown 了,并且 majority 数量的哨兵都容许主备切换,那么某个哨兵就会执行主备切换操做,此时首先要选举一个 slave 来,会考虑 slave 的一些信息:

跟 master 断开链接的时长
slave 优先级
复制 offset
run id
若是一个 slave 跟 master 断开链接的时间已经超过了 down-after-milliseconds 的 10 倍,外加 master 宕机的时长,那么 slave 就被认为不适合选举为 master。

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

接下来会对 slave 进行排序:

按照 slave 优先级进行排序,slave priority 越低,优先级就越高。
若是 slave priority 相同,那么看 replica offset,哪一个 slave 复制了越多的数据,offset 越靠后,优先级就越高。
若是上面两个条件都相同,那么选择一个 run id 比较小的那个 slave。

quorum 和 majority
每次一个哨兵要作主备切换,首先须要 quorum 数量的哨兵认为 odown,而后选举出一个哨兵来作切换,这个哨兵还须要获得 majority 哨兵的受权,才能正式执行切换。

若是 quorum < majority,好比 5 个哨兵,majority 就是 3,quorum 设置为 2,那么就 3 个哨兵受权就能够执行切换。

可是若是 quorum >= majority,那么必须 quorum 数量的哨兵都受权,好比 5 个哨兵,quorum 是 5,那么必须 5 个哨兵都赞成受权,才能执行切换。

configuration epoch
哨兵会对一套 redis master+slaves 进行监控,有相应的监控的配置。

执行切换的那个哨兵,会从要切换到的新 master(salve->master)那里获得一个 configuration epoch,这就是一个 version 号,每次切换的 version 号都必须是惟一的。

若是第一个选举出的哨兵切换失败了,那么其余哨兵,会等待 failover-timeout 时间,而后接替继续执行切换,此时会从新获取一个新的 configuration epoch,做为新的 version 号。

configuration 传播
哨兵完成切换以后,会在本身本地更新生成最新的 master 配置,而后同步给其余的哨兵,就是经过以前说的 pub/sub 消息机制。

这里以前的 version 号就很重要了,由于各类消息都是经过一个 channel 去发布和监听的,因此一个哨兵完成一次新的切换以后,新的 master 配置是跟着新的 version 号的。其余的哨兵都是根据版本号的大小来更新本身的 master 配置的。


本文的重点是你有没有收获与成长,其他的都不重要,但愿读者们能谨记这一点。同时我通过多年的收藏目前也算收集到了一套完整的学习资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货,但愿对想成为架构师的朋友有必定的参考和帮助

须要更详细架构师技能思惟导图和如下资料的能够加一下技术交流分享群:“708 701 457”免费获取