由经验而得的智慧,胜于学习而得的智慧。个人文章精选合集
更多文章分享请关注微信公众号 星河之码
Redis 哨兵集群实现高可用
哨兵的介绍
哨兵(sentinel)是Redis的高可用性(High Availability)的解决方案。主要有以下功能:
- 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
- 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
- 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上(将slave升为master)。
- 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
- 由一个或多个sentinel实例组成sentinel集群可以监控一个或多个主服务器和多个从服务器。
- 当主服务器进入下线状态时,sentinel可以将该主服务器下的某一从服务器升级为主服务器继续提供服
务,从而保证redis的高可用性。- 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意,涉及分布式选举问题。
- 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就没有意义了。
哨兵模式实现原理
- 哨兵至少需要 3 个实例,来保证自己的健壮性。
- 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了 2 个哨兵实例,quorum = 1。
+----+ +----+
| 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 节点哨兵集群是这样的:
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
配置 quorum=2
,如果 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机了,然后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移。
部署架构图
哨兵模式常见问题
一、 哨兵主备切换的数据丢失问题
主备切换的过程中,可能会导致数据丢失。
1.数据丢失的两种场景
-
异步复制导致的数据丢失
因为 master—>slave 的复制是异步的,可能有部分数据还没复制到 slave,master 就宕机了,此时这部分数据就丢失了。
-
脑裂导致的数据丢失
某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着。此时哨兵可能就会认为 master 宕机了,然后开启选举,将其他 slave 切换成了 master。这个时候,集群里就会有两个 master ,也就是所谓的脑裂。
此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。而新的 master 并没有后来 client 写入的数据,因此,这部分数据也就丢失了。
2.数据丢失问题的解决方案
进行如下配置:
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 丢失连接,那么
min-slaves-to-write
和min-slaves-max-lag
两个配置可以确保如果不能继续给指定数量的 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 上。
五、quorum 和 majority
- quorum:确认odown的最少的哨兵数量
- majority:授权进行主从切换的最少的哨兵数量
每次一个哨兵要做主备切换,首先需要 quorum 数量的哨兵认为 odown,然后选举出一个哨兵来做切换,这个哨兵还需要得到 majority 哨兵的授权,才能正式执行切换。
- 如果 quorum < majority,比如 5 个哨兵,majority 就是 3,quorum 设置为 2,那么就 3 个哨兵授权就可以执行切换。
- 但是如果 quorum >= majority,那么必须 quorum 数量的哨兵都授权,比如 5 个哨兵,quorum 是 5,那么必须 5 个哨兵都同意授权,才能执行切换。
六、slave->master 选举算法
如果某个 master 被认为 odown 了,而且 majority 数量的哨兵都允许主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个 slave 来,会考虑 slave 的一些信息:
-
跟 master 断开连接的时长
如果一个 slave 跟 master 断开连接的时间已经超过了
down-after-milliseconds
的 10 倍加上 master 宕机的时长之和,那么 slave 就被认为不适合选举为 master。断开连接的时间 > (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
-
slave 优先级
按照 slave 优先级进行排序,slave priority (权重)越低,优先级就越高。
-
复制 offset
如果 slave priority(权重) 相同,那么看 replica offset,哪个 slave 复制了越多的数据,offset 越靠后,优先级就越高。
-
run id
如果上面两个条件都相同,那么选择一个 run id 比较小的那个 slave。
七、configuration epoch
哨兵会对一套 redis master+slaves 进行监控,有相应的监控的配置。
- 执行切换的那个哨兵,会从要切换到的新 master(salve->master)那里得到一个 configuration epoch,这就是一个 version 号,每次切换的 version 号都必须是唯一的。
- 如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待 failover-timeout 时间,然后接替继续执行切换,此时会重新获取一个新的 configuration epoch,作为新的 version 号。
八、configuration 传播
哨兵完成切换之后,会在自己本地更新生成最新的 master 配置,然后同步给其他的哨兵,就是通过 pub/sub
消息机制。
因为各种消息都是通过一个 channel 去发布和监听的,所以一个哨兵完成一次新的切换之后,新的 master 配置是跟着新的 version 号的。其他的哨兵都是根据版本号的大小来更新自己的 master 配置的。