目录
前提须知,本篇是 Redis 主从复制,哨兵,集群三板斧的第二篇,建议同学先阅读过主从复制篇,再来观看哨兵篇,想必你会有更好的理解哦!如果你已经懂了主从复制原理,可以直接看正文。
1. Redis 哨兵是什么?
哨兵就像一个看守者,看守某个东西,在 Redis 中哨兵就是来看守服务器的状态的。
在主从复制服务器中,主服务器宕机了,从服务器不会上位替代,而是一直等待主服务器恢复正常,但是从服务器又没有写数据的权限,就会导致写数据能力丢失,业务就会受到影响。所以我们就需要想一个办法,在 master 主服务器宕机无法对外提供服务的时候,从 slave 从服务器中挑选一个新的 master 继续对外提供服务,保证我们业务的正常运行。
那么谁来做这件事呢?就是我们的本篇文章重点要说的 "哨兵"。
2. Redis 哨兵有什么用?
2.1 主动监控
哨兵可以监控主从 Redis 数据库运行是否正常;
2.2 消息通知
哨兵可以将故障转移的结果发送给客户端,让我们的运维人员知道哪台 redis 服务器可能出现了故障,以便于及时恢复;
2.3 故障转移
如果 master 主服务器发生了异常,那么哨兵就会自动进行主从切换,将其中一个 slave 从服务器作为新的 master 主服务器继续对外服务;
2.4 配置中心
客户端通过连接哨兵来获得当前 Redis 服务的主节点地址;
3. Redis 哨兵数量配备要求
在企业配备 Redis 哨兵的时候,也是有默认要求的,Redis 哨兵的数量应当不少于三台并且必须是奇数台。如果不能满足以上要求,使用哨兵会有潜在风险。为什么哨兵的数量要选择奇数,这和 Raft 算法选举 leader 哨兵领导者有关,下面我会专门说,这也是一个面试考点。
4. 哨兵配置文件详解
我们在安装 redis 之后它就已经给我们配备了哨兵的配置文件,就存放在 Redis 安装目录下,各位同学可以打开自己的 Redis 安装文件夹,如下所示,我使用 ls 命令展示我 redis-6.2.6 的所有文件,这里可以看到 redis.conf 配置文件,sentinel.conf 哨兵配置文件 等。
在面试和工作中,几乎很少会让你配置哨兵的配置信息,一般都会提前设定好,直接粘贴进 redis 的配置文件即可,但关于配置文件的各种配置的作用各位还是要记清楚的。
[root@localhost redis-6.2.6]# ls
00-RELEASENOTES CONDUCT COPYING INSTALL MANIFESTO redis.conf runtest runtest-moduleapi sentinel.conf tests utils
BUGS CONTRIBUTING deps Makefile README.md redis.log runtest-cluster runtest-sentinel src TLS.md
在 Redis 哨兵配置文件 sentinel.conf 中,有几个比较重要的配置项,我拿出来放到下边。
1:bind:0.0.0.0;
2:daemonize yes (yes开启表示后台运行);
3:protected-mode no(no 表示关闭保护模式);
4:port 26379 (26379为哨兵服务器端口,6379为 redis 服务器端口,两者不一样,各位同学要分清哦!);
5:logfile "文件路径"(logfile为哨兵日志文件,和redis日志文件不是同一个,后面双引号内部可以自定义日志文件路径);
6:sentinel monitor <mastername> <ip> <redis-port> <quorum>
mastername 为主机名,可自定义设置,IP 就是要监控的主机IP,redis-port 就是主机的端口号,quorum 为投票数;
举例如 sentinel monitor mymaster 121.41.67.181 6379 2
7:sentinel auth-pass <mastername> <password>
mastername 就是主机名称,和上面的保持一致,password 为监控主机的密码;
举例如 sentinel auth-pass mymaster 123456
5. quorum 投票数详解
5.1 quorum 的含义
在上面第四点我重点标注了 quorum 为投票数,这是我们理解哨兵机制必须要掌握的一个知识点。
总结为一句话就是,"至少有 quorum 个哨兵认为 master 主机该下线的时候,master 主机就要客观下线并进行故障转移"。
5.2 网络抖动导致主观下线
我们知道网络是不太可靠的,而哨兵与主机之间又是通过网络发送心跳包来确认主机是否还在正常运行。在 sentinel.conf 配置文件中有这一配置项,可以自定义配置时长,假设为 30s,如果网络抖动导致丢包,又或是 master 主机真的出现了问题,导致一个哨兵在 30s 时间内应该收到 master 主机发送过来的心跳反馈但实际却没有收到,那么当前这台哨兵就会认为主机可能已经发生故障了,当前哨兵就会对它投票认为它出故障该下线了换新的服务器上线作为主机,但实际这台主机还没有下线,只是当前这一台没有接收到心跳包的哨兵服务器认为它出故障该下线了,这就是主观下线(Subjectively Down)。
下面这一段是我从 sentinel.conf 配置文件中摘下来的关于主观下线的部分注释和配置,上面是这段英文的翻译
主服务器(或任何附加的副本或哨兵)应达到的毫秒数
# 无法访问(例如,不接受对 PING 的回复,连续,对于
# 指定时间段),以便将其视为处于S_DOWN状态(主观上
# 向下线)。
# Number of milliseconds the master (or any attached replica or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
# sentinel down-after-milliseconds <master-name> <milliseconds> // 配置示例
# Default is 30 seconds. // 从配置文件中不难看出 redis 默认是 30 秒
sentinel down-after-milliseconds mymaster 30000
5.3 quorum 票数达到设定值客观下线
我们假设每台哨兵只能投一票,quorum 为投票数,如果三台哨兵中有哨兵没有收到 master 主机发送过来的心跳包,那么当前哨兵就会与其余两台哨兵进行协商沟通,没有收到心跳包的哨兵会投一票给当前 master,认为它已经挂了,如果另外两台哨兵中又有一个也没有收到心跳反馈包,那么它也会投一票,此时已经有两票了认为 master 已经挂了,而我们设置的 quorum 正好是 2,已经达到下线要求了;就算剩余的一台哨兵能收到主机发送的心跳包,没有进行投票,但是少数服从多数,两台哨兵都认为主机已经出故障该下线了,那么主机就要客观下线。客观下线就是真的下线了,不能再继续对外提供服务。
我设置 3 台哨兵,quorum 值应该设置为 2,超过一半;
如果有 5 台哨兵,quorum 值至少应该设置为 3,超过一半;
如果有 7 台哨兵,quorum 值至少应该设置为 4,超过一半;
少数服从多数,至少要一半以上的哨兵都认为主观下线了,主机才能真正客观下线(Objectively Down),不能说一台哨兵认为 master 该下线了就真的让它下线,只要投票数没有达到 quorum 值都只是主观下线,没有真的下线。
6. 最好让所有 redis 服务器密码一致
我们在做主从复制架构的时候,所有从机都要配置主机的密码,主机不需要配置。但现在如果我们加入了哨兵机制,主机也要配备密码,并且不管主机还是从机,密码最好保持一致。
原因如下:
(1)一旦当前 master 主机挂了,slave从机 会重新选举新的服务器作为主机,如果配置文件内的主机密码与新选举出来的主机密码不一致,则新选举的主机会拒绝访问;
(2)再者,原来下线的主机重新上线之后,就会变成从机,在访问新主机就需要知道新主机的密码,否则也会拒绝访问,所以建议所有 redis 服务器密码设置为一样的;
7. 什么是 leader 哨兵领导者
以下是 sentinel.conf 配置文件中关于 leader 哨兵领导者选举的部分注释。
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
翻译过来的大致意思就是,"一旦主节点已经被哨兵集群投票并通过认定当前 master 已经主观下线了,那么哨兵内部就会选举出一个哨兵领导 leader,然后由 leader 进行新的 master 节点选取和故障转移。"
记住重点:新的 master 服务器是由哨兵集群中选出来的 leader 选举的,并不是由哨兵集群决定的,哨兵集群只负责选择哨兵领导者 leader,由 leader 去选取新的 master。
为什么会有 leader 呢?很好理解,假如说现在 master 主机挂了,要重新选择一个 master,谁来选呢?三个哨兵发生争吵怎么办,哨兵A说凭什么你来选,我能力强我来选,哨兵B说我能力强我来选,哨兵C说我能力更强我来选,你们两个退后。
所以在选取新的 master 之前,哨兵内部会先选出一个 leader 哨兵领导者,由 leader 领导者选取新的从服务器作为 master。
8. Raft 算法选举 leader 哨兵领导者
Raft 算法底层其实非常的复杂,Raft 算法核心思想在微服务中广泛使用,虽然在别的中间件中可能是另一种算法,但算法的核心思想都是一样的。这里我做了相应的简化,想深入了解的同学可以搜索相关的资料。
如下图,Raft 算法的核心思想就是先到先得,并且一个哨兵只能投一票,以下图为例,
(1)sentinel1 哨兵给 sentinel2 和 sentinel3 分别发送信息,说我想当领导,你们两个都投我;
(2)sentinel2 接收到了 sentinel1 的投票请求,投了 sentinel1 一票,但 sentinel2 自己也想当领导, sentinel2 给 sentinel1 和 sentinel3 发信息说你们两个投我也想当领导;
(3)sentinel3 在接收到了 sentinel1 和 sentinel2 的投票请求后,因为 sentinel3 先接收到了sentinel1 的投票请求,所以 sentinel3 将自己的票投给了 sentinel1,当接收到 sentinel2 的投票请求后,就不能再给 sentinel2 投票了,因为它已经投过票了;但是 sentinel3 它也想当领导,就给 sentinel1 和 sentinel2 发投票请求让他们两个也投我票;
(4)sentinel1 接收到了 sentinel2 和 sentinel3 的投票,自己有两票,又接收到了 sentinel2 的投票请求,于是 sentinel1 将自己的一票投给了 sentinel2,当再次接收到 sentinel3 的投票请求时,就不能重复投票;
(5)经过投票之后,sentinel1获得了两票,sentinel2获得了一票,sentinel3获得了0票,恭喜sentinel1 称为 leader 哨兵领导者;
(6)注意一点,在实际 leader 选取过程中,过程并不一定是我上述的样子,我列举的只是其中一种情况,也有可能 sentinel2 票最多,也有可能 sentinel3 最多,就看谁先获得投票反馈了,所以说先到先得;
9. leader 选取新 master 的规则
leader 选取完毕之后,哨兵领导者 sentinel1 就会在几个 slave 从服务器中选取一个作为新的 master 主机。而 slave 从服务器也是一样的,凭什么都是 slave 从服务器,它就能当 master 做我的老大,我要当老大,我不服它。
因此,leader 哨兵选取有一定的规则,自然而然新的 master 选取也是有规则的嘛。
新 master 的选取规则主要看三点,
(1)首先第一点:看谁的优先级更高。
虽然都是从服务器,但它们自己的 sentinel.conf 配置文件内还有一个优先级属性,slave-priority或replica-priority,redis6版本和redis7版本名字不太一样,但代表的含义都是一样的,我们可以对不同的从服务器优先级属性进行配置,谁的优先级高,谁就会优先被选举为新的 master。
(2)其次第二点:如果优先级都一样大,再看谁的复制偏移量最大。
举个例子,slave 从服务器会同步 master 主服务器上的数据,假设 master 在挂掉之前一共有10个数据,slave从服务器A也许因为网络波动只同步过来 9 个数据,丢失了一个。而slave从服务器B把 10 个数据全都同步过来了。那么A的偏移量就是9,B的偏移量就是10;明显B比A能力更强,并且升级为 master 花费的资源更少,而A在同步时还要再添加一条数据,需要花费更多资源,所以理所当然B更有资格也更适合成为新的 master。
(3)最后第三点:如果优先级一样大,偏移量也一样,最后比较RunID的ASCLL码。
每个从机服务器都是一个单独的服务嘛,那它就有一个独一无二的运行ID,比较各个从服务器的ASCLL码,谁小谁就当新的 master 主机。
10. 从机变成 master 的过程?
OK,leader经过了三条规则的筛选之后,就能从众多从服务器中确定出来哪个做新的 master了,确定好了之后,还要经过下面的步骤才能彻底完成 master 的选取到完成。
(1)leader会对新选举出来的 master 执行 slaveof no one 命令使其独立为master;
(2)leader 对其他的 slave 从服务器发送命令,让剩余的 slave 变成新 master 的 slave;
(3)之前下线的老 master 重新上线之后,就不再是 master了,leader 会将其降级为 slave,变成新 master 的 slave从机;
11. 解答哨兵数量配备问题
问题一,为什么哨兵不能是一台?
因为哨兵本身它也是一个服务器,如果哨兵出现故障挂了,同时 Redis 主机也出现了故障挂了,那么就没有人能来看守并选出新的主master数据库了,会导致我们的哨兵失效,业务还是会受到影响。
问题二,为什么不能是两台?
从刚才的 Raft 算法中可以看出,如果是两台哨兵,互相投票,最后统计的时候都是一票,无法选出 leader,所以两台哨兵的做法不可取。
问题三,为什么哨兵必须是奇数台?
为了方便投票,和刚才的问题二一样的原理,如果有偶数台机器,两两之间互相投票,到最后很有可能会有多台哨兵得票数相等,无法选出最终的 leader,但如果我们采用奇数台哨兵,则可以大大降低无法选出 leader 的情况发生。
12. 哨兵的使用建议
(1)哨兵数量是少在三个以上并且为奇数;
(2)哨兵配备在不同的服务器,硬件最好保持一致,不要这台哨兵的服务器16GB内存,那台哨兵32GB内存,最好保持一致,避免外界环境对我们的 master 选举造成不必要的干扰;
(3)生产过程中经常会使用 Docker 容器化技术部署哨兵集群,一定一定一定要注意端口正确映射;
(4)哨兵集群+主从复制并不能完全保证数据不丢失,因为老 master 下线到新 mater 选举到上线期间是有一定的延迟的,或许几秒,或许几十秒,在此期间我们的 redis 只能对外提供读的业务,不能写数据,所以选举期间有写数据请求,数据会丢失。
(5)主从复制+哨兵集群的这种方式虽然不错,但还是有一定的局限性,拿最典型的来说,只能有一个 master 主机对外提供写数据操作,而 slave 从机只能进行读数据操作,为了能让更多的 Redis 都可以进行读写数据操作,所以就有了集群。在 Redis 集群中,可以设置多台 master,也可以设置多台 slave,同时集群内部自带哨兵机制,不需要我们自己配,非常方便,redis 集群我写了另外一篇文章,感兴趣的小伙伴可以跳转观看。