文章将首先介绍哨兵的作用和架构;然后讲述哨兵系统的部署方法,以及通过客户端访问哨兵系统的方法;然后简要说明哨兵实现的基本原理;最后给出关于哨兵实践的一些建议。(注:文章内容基于 Redis 3.0 版本)
哨兵的作用和架构
哨兵的作用
在介绍哨兵之前,首先从宏观角度回顾一下 Redis 实现高可用相关的技术。
它们包括:持久化、复制、哨兵和集群,其主要作用和解决的问题是:
- 持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
- 复制:复制是高可用 Redis 的基础,哨兵和集群都是在复制基础上实现高可用的。
复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
- 哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
- 集群:通过集群,Redis 解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。
下面说回哨兵,,即 哨兵,在 版本开始引入。哨兵的核心功能是主节点的自动故障转移。
下面是 Redis 官方文档对于哨兵功能的描述:
- 监控():哨兵会不断地检查主节点和从节点是否运作正常。
- 自动故障转移():当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
- 配置提供者():客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址。
- 通知():哨兵可以将故障转移的结果发送给客户端。
其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。
使用的是 Redis 提供的底层接口,而客户端则对这些接口、功能进行了封装,以便充分利用哨兵的配置提供者和通知功能。
哨兵的架构
典型的哨兵架构图如下所示:
部署哨兵节点
哨兵节点本质上是特殊的 Redis 节点。3 个哨兵节点的配置几乎是完全一样的,主要区别在于端口 的不同(26379/26380/26381)。
下面以 26379 节点为例,介绍节点的配置和启动方式,配置部分尽量简化,更多配置会在后面介绍。
其中, 配置的含义是:该哨兵节点监控 这个主节点。
该主节点的名称是 ,最后的 2 的含义与主节点的故障判定有关:至少需要 2 个哨兵节点同意,才能判定主节点故障并进行故障转移。
哨兵节点的启动有两种方式,二者作用是完全相同的:
按照上述方式配置和启动之后,整个哨兵系统就启动完毕了,可以通过 连接哨兵节点进行验证。
如下图所示:可以看出 哨兵节点已经在监控 主节点(即),并发现了其 2 个从节点和另外 2 个哨兵节点。
带有 的参数与配置纪元有关(配置纪元是一个从 开始的计数器,每进行一次领导者哨兵选举,都会 ;领导者哨兵选举是故障转移阶段的一个操作,在后文原理部分会介绍)。
演示故障转移
这一小节将演示当主节点发生故障时,哨兵的监控和自动故障转移功能。
(1)首先,使用 Kill 命令杀掉主节点:
对于主从节点,主要是 配置的变化:新的主节点没有了 配置,其从节点则 新的主节点。
对于哨兵节点,除了主从节点信息的变化,纪元()也会变化,下图中可以看到纪元相关的参数都 +1 了。
增加对哨兵的监听:这样当发生故障转移时,客户端便可以收到哨兵的通知,从而完成主节点的切换。
具体做法是:利用 提供的发布订阅功能,为每一个哨兵节点开启一个单独的线程,订阅 频道,当收到消息时,重新初始化连接池。
小结
通过客户端原理的介绍,我们可以加深对哨兵功能的理解。
配置提供者:客户端可以通过 获取主节点信息,在这里哨兵起到的作用就是配置提供者。
需要注意的是,哨兵只是配置提供者,而不是代理。二者的区别在于:
- 如果是配置提供者,客户端在通过哨兵获得主节点信息后,会直接建立到主节点的连接,后续的请求(如 )会直接发向主节点。
- 如果是代理,客户端的每一次请求都会发向哨兵,哨兵再通过主节点处理请求。
举一个例子可以很好的理解哨兵的作用是配置提供者,而不是代理。在前面部署的哨兵系统中,将哨兵节点的配置文件进行如下修改:
然后,将前述客户端代码在局域 的另外一台机器上运行,会发现客户端无法连接主节点。
这是因为哨兵作为配置提供者,客户端通过它查询到主节点的地址为 ,客户端会向 建立 连接,自然无法连接。如果哨兵是代理,这个问题就不会出现了。
通知:哨兵节点在故障转移完成后,会将新的主节点信息发送给客户端,以便客户端及时切换主节点。
哨兵实现的基本原理
前面介绍了哨兵部署、使用的基本方法,本部分介绍哨兵实现的基本原理。
哨兵节点支持的命令
哨兵节点作为运行在特殊模式下的 节点,其支持的命令与普通的 节点不同。
在运维中,我们可以通过这些命令查询或修改哨兵系统;不过更重要的是,哨兵系统要实现故障发现、故障转移等各种功能,离不开哨兵节点之间的通信。
而通信的很大一部分是通过哨兵节点支持的命令来实现的。下面介绍哨兵节点支持的主要命令。
基础查询
通过这些命令,可以查询哨兵系统的拓扑结构、节点信息、配置信息等:
- :获取监控的所有主节点的基本信息。
- :获取监控的所有主节点的详细信息。
- :获取监控的主节点 的详细信息。
- :获取监控的主节点 的从节点的详细信息。
- :获取监控的主节点 的哨兵节点的详细信息。
- :获取监控的主节点 的地址信息,前文已有介绍。
- :哨兵节点之间可以通过该命令询问主节点是否下线,从而对是否客观下线做出判断。
增加/移除对主节点的监控
:与部署哨兵节点时配置文件中的 功能完全一样,不再详述。
:取消当前哨兵节点对主节点 的监控。
强制故障转移
:该命令可以强制对 执行故障转移,即便当前的主节点运行完好。
例如,如果当前主节点所在机器即将 废,便可以提前通过命令进行故障转移。
基本原理
关于哨兵的原理,关键是了解以下几个概念。
定时任务
每个哨兵节点维护了 3 个定时任务,定时任务的功能分别如下:
- 通过向主从节点发送 命令获取最新的主从结构。
- 通过发布订阅功能获取其他哨兵节点的信息。
- 通过向其他节点发送 命令进行心跳检测,判断是否下线。
主观下线
在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。
顾名思义,主观下线的意思是一个哨兵节点“主观地”判断下线;与主观下线相对应的是客观下线。
客观下线
哨兵节点在对主节点进行主观下线后,会通过 命令询问其他哨兵节点该主节点的状态。
如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。
需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。
选举领导者哨兵节点
当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。
监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是 算法。
算法的基本思路是先到先得:即在一轮选举中,哨兵 A 向 B 发送成为领导者的申请,如果 B 没有同意过其他哨兵,则会同意 A 成为领导者。
选举的具体过程这里不做详细描述,一般来说,哨兵选择的过程很快,谁先完成客观下线,一般就能成为领导者。
故障转移
选举出的领导者哨兵,开始进行故障转移操作,该操作大体可以分为 3 个步骤:
- 在从节点中选择新的主节点:选择的原则是,首先过滤掉不健康的从节点,然后选择优先级最高的从节点(由 指定)。
如果优先级无法区分,则选择复制偏移量最大的从节点;如果仍无法区分,则选择 最小的从节点。
- 更新主从状态:通过 命令,让选出来的从节点成为主节点;并通过 命令让其他节点成为其从节点。
- 将已经下线的主节点(即 )设置为新的主节点的从节点,当 重新上线后,它会成为新的主节点的从节点。
通过上述几个关键概念,可以基本了解哨兵的工作原理。为了更形象的说明,下图展示了领导者哨兵节点的日志,包括从节点启动到完成故障转移。

哨兵配置与实践建议
哨兵配置
下面介绍与哨兵相关的几个配置。
是哨兵最核心的配置,在前文讲述部署哨兵节点时已说明,其中: 指定了主节点名称, 和 指定了主节点地址, 是判断主节点客观下线的哨兵数量阈值。
当判定主节点下线的哨兵数量达到 时,对主节点进行客观下线。建议取值为哨兵数量的一半加 1。
与主观下线的判断有关:哨兵使用 命令对其他节点进行心跳检测。
如果其他节点超过 配置的时间没有回复,哨兵就会将其进行主观下线,该配置对主节点、从节点和哨兵节点的主观下线判定都有效。
的默认值是 ,即 ;可以根据不同的 络环境和应用要求来调整。
值越大,对主观下线的判定会越宽松,好处是误判的可能性小,坏处是故障发现和故障转移的时间变长,客户端等待的时间也会变长。
例如,如果应用对可用性要求较高,则可以将值适当调小,当故障发生时尽快完成转移;如果 络环境相对较差,可以适当提高该阈值,避免频繁误判。
与故障转移之后从节点的复制有关:它规定了每次向新的主节点发起复制操作的从节点个数。
例如,假设主节点切换完成之后,有 3 个从节点要向新的主节点发起复制;如果 parallel-syncs=1,则从节点会一个一个开始复制;如果 ,则 个从节点会一起开始复制。
取值越大,从节点完成复制的时间越快,但是对主节点的 络负载、硬盘负载造成的压力也越大;应根据实际情况设置。
例如,如果主节点的负载较低,而从节点对服务可用的要求较高,可以适量增加 取值。 的默认值是 。
与故障转移超时的判断有关,但是该参数不是用来判断整个故障转移阶段的超时,而是其几个子阶段的超时。
例如如果主节点晋升从节点时间超过 ,或从节点向新的主节点发起复制操作的时间(不包括复制数据的时间)超过 ,都会导致故障转移超时失败。
的默认值是 ,即 ;如果超时,则下一次该值会变为原来的 2 倍。
除上述几个参数外,还有一些其他参数,如安全验证相关的参数,这里不做介绍。
实践建议
哨兵节点的数量应不止一个,一方面增加哨兵节点的冗余,避免哨兵本身成为高可用的瓶颈;另一方面减少对下线的误判。此外,这些不同的哨兵节点应部署在不同的物理机上。
哨兵节点的数量应该是奇数,便于哨兵通过投票做出“决策”:领导者选举的决策、客观下线的决策等。
各个哨兵节点的配置应一致,包括硬件、参数等;此外,所有节点都应该使用 ntp 或类似服务,保证时间准确、一致。
哨兵的配置提供者和通知客户端功能,需要客户端的支持才能实现,如前文所说的 Jedis;如果开发者使用的库未提供相应支持,则可能需要开发者自己实现。
当哨兵系统中的节点在 Docker(或其他可能进行端口映射的软件)中部署时,应特别注意端口映射可能会导致哨兵系统无法正常工作。
因为哨兵的工作基于与其他节点的通信,而 Docker 的端口映射可能导致哨兵无法连接到其他节点。
例如,哨兵之间互相发现,依赖于它们对外宣称的 IP 和 port,如果某个哨兵 A 部署在做了端口映射的 Docker 中,那么其他哨兵使用 A 宣称的 port 无法连接到 A。
总结
在主从复制的基础上,哨兵引入了主节点的自动故障转移,进一步提高了 Redis 的高可用性。
但是哨兵的缺陷同样很明显:哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要我们对从节点做额外的监控、切换操作。
此外,哨兵仍然没有解决写操作无法负载均衡、及存储能力受到单机限制的问题;这些问题的解决需要使用集群,我将在后面的文章中介绍, 。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!