建议前面文章没看过的同窗先看下前面的文章:java
「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」node
「老司机带你玩转面试(2):Redis 过时策略以及缓存雪崩、击穿、穿透」git
「老司机带你玩转面试(3):Redis 高可用之主从模式」github
前面介绍了 Redis 的主从模式,主从模式只能实现读高可用,致命的弱点是写没法高可用,一旦 master 节点挂了,整个集群将没法写入数据,这并不符合咱们对 Redis 高可用集群的指望。面试
那么,是否是有一种方法,能够作到不只仅读高可用,写同样要高可用,固然有,这就是咱们今天要介绍的哨兵模式。redis
哨兵模式能够理解成主从模式的一个升级版,主从模式 master 节点和 slave 节点是一开始就定好的,而在哨兵模式中, master 节点是能够转移,一旦发现当前的 master 节点挂掉,经过选举能够指定一个 slave 节点晋升成为 master ,保证在任何状况下,都有 master 节点能够支持写入操做,也间接实现了写高可用。shell
哨兵模式能够看作是前面主从模式的一个升级版,主从模式没有故障转移, master 节点挂了就挂了,而哨兵模式就是为了解决这个问题而出现的。缓存
假如咱们如今有两个哨兵实例,就长下面这样:网络
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
复制代码
再了解两个参数: quorum 、 majority架构
在只有两个节点的状况下,若是 master 宕机, s1 和 s2 中只要有 1 个哨兵认为 master 宕机了,就能够进行切换,同时 s1 和 s2 会选举出一个哨兵来执行故障转移。
这时,须要 majority,也就是大多数哨兵都是运行的。
因此此时,若是此时仅仅是 M1 进程宕机了,哨兵 s1 正常运行,那么故障转移是 OK 的。可是若是是整个 M1 和 S1 运行的机器宕机了,那么哨兵只有 1 个,此时就没有 majority 来容许执行故障转移,虽然另一台机器上还有一个 R1,可是故障转移不会执行。
因此就有了如下这一条建议:
由于在进行选举的时候,须要超过一半的哨兵赞成,也就是 majority 。
2 个哨兵,majority=2
3 个哨兵,majority=2
4 个哨兵,majority=2
5 个哨兵,majority=3
6 个哨兵,majority=3
7 个哨兵,majority=4
...
复制代码
能够看到,只有在奇数的时候,是能够最大化的利用 majority 数量。
经典的三哨兵模型下面这样:
+----+
| M1 |
| S1 |
+----+
|
+----+ | +----+
| R2 |----+----| R3 |
| S2 | | S3 |
+----+ +----+
复制代码
若是 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 能够一致认为 master 宕机了,而后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,因此还剩下的 2 个哨兵运行着,就能够容许执行故障转移。
首先先说一个结论:
而后咱们再说两种会致使数据丢失的状况:
第一种:是异步复制可能会致使数据丢失
因为 master 向 salve 复制数据是异步,有可能,有部分数据尚未向 salve 复制,这时 master 宕机了,那么这部分数据就丢失了。
第二种:脑裂致使的数据丢失
脑裂是指,因为网络波动或者其余因素影响, master 所在的机器忽然间没法被其余哨兵梭访问到,可是实际上这个 master 节点还在正常运行中。
此时哨兵会觉得这个 master 节点已经宕机,开始进行新的 master 节点的选举,将其余的 salve 节点切换成了 master 节点,这时集群中就会存在两个 master 节点,也就是脑裂产生了。
这时虽然产生了新的 master 节点,可是客户端可能还没进行切换,还在像老的 master 写数据,可是当老的 master 恢复访问的时候,会被做为一个 salve 挂载到新的 master 节点上,本身的数据会被清空,从新重新的 master 复制数据,而在脑裂过程当中写入老的 master 的数据就这么没了。
数据丢失的解决方案有么?没有,由于这个问题是客观存在的,咱们解决不了这个问题,只能尽可能的去减小这个问题带来的损失,这时,可使用下面这两个配置:
min-slaves-to-write 1
min-slaves-max-lag 10
复制代码
这两个配置的意思是:
两个参数的意思:
(1) 减小异步复制的数据丢失:
有了 min-slaves-max-lag
这个配置,就能够确保说,一旦 slave 复制数据和 ack 延时太长,就认为可能 master 宕机后损失的数据太多了,那么就拒绝写请求,这样能够把 master 宕机时因为部分数据未同步到 slave 致使的数据丢失下降的可控范围内。
(2) 减小脑裂的数据丢失:
若是一个 master 出现了脑裂,跟其余 slave 丢了链接,那么上面两个配置能够确保说,若是不能继续给指定数量的 slave 发送数据,并且 slave 超过 10 秒没有给本身 ack 消息,那么就直接拒绝客户端的写请求。
这样脑裂后的旧 master 就不会接受 client 的新数据,也就避免了数据丢失。
上面的配置就确保了,若是跟任何一个 slave 丢了链接,在 10 秒后发现没有 slave 给本身 ack ,那么就拒绝新的写请求
所以在脑裂场景下,最多就丢失 10 秒的数据。
sdown 达成的条件很简单,若是一个哨兵 ping 一个 master,超过了 is-master-down-after-milliseconds
指定的毫秒数以后,就主观认为 master 宕机了;若是一个哨兵在指定时间内,收到了 quorum 数量的其它哨兵也认为那个 master 是 sdown 的,那么就认为是 odown 了。