使用Redis Sentinel实现高可用性

非集群Redis的高可用性

Redis Sentinel为不使用Redis集群的Redis提供高可用性。

Redis Sentinel还提供其他辅助任务,例如监控,通知并充当客户端的配置提供程序。

以下是Sentinel功能的完整列表,从宏观层面(即总体情况)来看。

  • 监控。Sentinel不断检查您的主服务器和副本实例是否按预期工作。
  • 通知。Sentinel可以通过API通知系统管理员或其他计算机程序,以指示监控的Redis实例之一出现了问题。
  • 自动故障转移。如果主服务器无法按预期工作,Sentinel可以启动故障转移过程,其中副本将被提升为主服务器,其他额外的副本将被重新配置以使用新的主服务器,并且使用Redis服务器的应用程序将收到有关连接时要使用的新的地址的信息。
  • 配置提供者。 Sentinel 充当客户端服务发现的权威来源:客户端连接到 Sentinel 以询问负责特定服务的当前 Redis 主节点的地址。 如果发生故障转移,Sentinel 将报告新地址。

Sentinel 作为分布式系统

Redis Sentinel 是一个分布式系统

Sentinel 本身被设计为在一个配置中运行,在该配置中,多个 Sentinel 进程协同工作。 拥有多个协同工作的 Sentinel 进程的优势如下:

  1. 当多个 Sentinel 同意某个给定主节点不再可用时,就会执行故障检测。 这降低了误报的可能性。
  2. 即使并非所有 Sentinel 进程都正常工作,Sentinel 也能正常工作,这使得系统对故障具有鲁棒性。 毕竟,拥有一个本身是单点故障的故障转移系统没有意义。

Sentinel、Redis 实例(主节点和从节点)以及连接到 Sentinel 和 Redis 的客户端的总和也是一个具有特定属性的更大的分布式系统。 在本文档中,将逐步介绍概念,从理解 Sentinel 的基本属性所需的必要信息开始,到更复杂的信息(可选)以了解 Sentinel 的工作原理。

Sentinel 快速入门

获取 Sentinel

Sentinel 的当前版本称为Sentinel 2。 它是在初始 Sentinel 实现的基础上重写的,使用了更强大且更容易预测的算法(在本文档中进行了解释)。

自 Redis 2.8 开始,Redis Sentinel 的稳定版本已发布。

新的开发工作在不稳定分支中进行,新功能有时会在被认为稳定后立即被移植到最新的稳定分支中。

与 Redis 2.6 一起发布的 Redis Sentinel 版本 1 已被弃用,不应使用。

运行 Sentinel

如果您使用的是redis-sentinel可执行文件(或者您有一个指向redis-server可执行文件的相同名称的符号链接),则可以使用以下命令行运行 Sentinel:

redis-sentinel /path/to/sentinel.conf

否则,您可以直接使用redis-server可执行文件,以 Sentinel 模式启动它

redis-server /path/to/sentinel.conf --sentinel

这两种方法都一样。

但是,在运行 Sentinel 时,必须使用配置文件,因为系统将使用该文件来保存当前状态,该状态将在重启时重新加载。 如果未提供配置文件,或者配置文件路径不可写,Sentinel 将拒绝启动。

Sentinel 默认监听 TCP 端口 26379 的连接,因此,为了使 Sentinel 正常工作,您的服务器的端口 26379必须打开,以便接收来自其他 Sentinel 实例的 IP 地址的连接。 否则,Sentinel 无法通信,也无法就该做什么达成一致,因此将永远不会执行故障转移。

在部署 Sentinel 之前需要了解的一些基本内容

  1. 您至少需要三个 Sentinel 实例才能进行稳健的部署。
  2. 这三个 Sentinel 实例应放置在被认为以独立方式发生故障的计算机或虚拟机中。 因此,例如,不同的物理服务器或在不同可用区中执行的虚拟机。
  3. Sentinel + Redis 分布式系统不保证在故障期间保留已确认的写入,因为 Redis 使用异步复制。 但是,有一些方法可以部署 Sentinel,使丢失写入的窗口限制在某些时刻,而其他方法则安全性较低。
  4. 您的客户端需要支持 Sentinel。 流行的客户端库支持 Sentinel,但并非所有库都支持。
  5. 如果您不定期在开发环境中测试(或者更好的是,如果可以的话,在生产环境中测试),则没有安全的 HA 设置。 您可能存在一个错误配置,只有在为时已晚时才会显现出来(在凌晨 3 点您的主节点停止工作时)。
  6. Sentinel、Docker 或其他形式的网络地址转换或端口映射应谨慎混合使用:Docker 执行端口重新映射,破坏 Sentinel 对其他 Sentinel 进程的自动发现以及主节点的从节点列表。 有关更多信息,请查看本文档后面的关于Sentinel 和 Docker的部分

配置 Sentinel

Redis 源代码分发包中包含一个名为sentinel.conf的文件,它是一个自文档化的示例配置文件,您可以使用它来配置 Sentinel,但是一个典型的最小配置文件如下所示

sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5

您只需要指定要监控的主节点,并为每个独立的主节点(可能具有任意数量的从节点)指定不同的名称。 无需指定从节点,这些从节点会自动发现。 Sentinel 将使用有关从节点的其他信息自动更新配置(以便在重启时保留信息)。 每次从节点在故障转移期间被提升为主节点,以及每次发现新的 Sentinel 时,配置也会被重写。

上面的示例配置基本上监控两组 Redis 实例,每组实例都包含一个主节点和一个未定义数量的从节点。 一组实例称为mymaster,另一组称为resque

sentinel monitor语句的参数含义如下

sentinel monitor <master-name> <ip> <port> <quorum>

为了清晰起见,让我们逐行检查配置选项的含义

第一行用于告诉 Redis 监控一个名为mymaster的主节点,该主节点位于地址 127.0.0.1,端口 6379,法定人数为 2。 一切都很明显,但法定人数参数除外

  • 法定人数是指需要就主节点不可达的事实达成一致的 Sentinel 数量,以便真正将主节点标记为失败,并最终在可能的情况下启动故障转移过程。
  • 但是,法定人数仅用于检测故障。 为了实际执行故障转移,其中一个 Sentinel 需要被选为主故障转移领导者,并被授权继续执行。 这只有在大多数 Sentinel 进程投票的情况下才会发生。

例如,如果您有 5 个 Sentinel 进程,并且给定主节点的法定人数设置为 2,那么会发生以下情况:

  • 如果两个 Sentinel 同时同意主节点不可达,那么这两个 Sentinel 中的一个会尝试启动故障转移。
  • 如果至少有三个 Sentinel 可达,则故障转移将被授权并实际开始。

实际上,这意味着在发生故障期间,如果大多数 Sentinel 进程无法通信(即,少数分区中没有故障转移),则 Sentinel 永远不会启动故障转移

其他 Sentinel 选项

其他选项几乎总是采用以下形式

sentinel <option_name> <master_name> <option_value>

并用于以下目的

  • down-after-milliseconds是实例在 Sentinel 开始认为它已关闭之前不应该可达的时间(以毫秒为单位)(即,它没有回复我们的 PING 或它回复了错误)。
  • parallel-syncs设置了在故障转移后可以同时重新配置以使用新主节点的从节点数量。 数字越小,故障转移过程完成所需的时间越长,但是,如果从节点配置为提供旧数据,您可能不希望所有从节点同时与主节点重新同步。 虽然复制过程对于从节点来说大多是非阻塞的,但有一个时刻,它会停止从主节点加载大量数据。 您可能希望确保一次只有一个从节点不可达,将此选项设置为 1。

本文档的其他部分以及 Redis 分发包中提供的示例sentinel.conf文件中描述了其他选项。

可以在运行时修改配置参数

  • 使用SENTINEL SET修改特定于主节点的配置参数。
  • 使用SENTINEL CONFIG SET修改全局配置参数。

有关更多信息,请参见在运行时重新配置 Sentinel部分

Sentinel 部署示例

现在您已经了解了有关 Sentinel 的基本信息,您可能想知道应该将 Sentinel 进程放置在何处,需要多少个 Sentinel 进程,等等。 本节展示了一些部署示例。

我们使用 ASCII 艺术来以图形格式向您展示配置示例,这就是不同符号的含义

+--------------------+
| This is a computer |
| or VM that fails   |
| independently. We  |
| call it a "box"    |
+--------------------+

我们在框内写出它们运行的内容

+-------------------+
| Redis master M1   |
| Redis Sentinel S1 |
+-------------------+

不同的框通过线连接,表示它们能够通信

+-------------+               +-------------+
| Sentinel S1 |---------------| Sentinel S2 |
+-------------+               +-------------+

网络分区显示为使用斜杠的中断线

+-------------+                +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 |
+-------------+                +-------------+

另请注意

  • 主节点称为 M1、M2、M3、...、Mn。
  • 从节点称为 R1、R2、R3、...、Rn(R 代表从节点)。
  • Sentinel 称为 S1、S2、S3、...、Sn。
  • 客户端称为 C1、C2、C3、...、Cn。
  • 当某个实例由于 Sentinel 操作而更改角色时,我们将它放在方括号内,因此 [M1] 表示由于 Sentinel 干预而现在成为主节点的实例。

请注意,我们永远不会显示仅使用两个 Sentinel 的设置,因为 Sentinel 始终需要与大多数 Sentinel 通信才能启动故障转移。

示例 1:仅有两个 Sentinel,不要这样做

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

Configuration: quorum = 1
  • 在此设置中,如果主节点 M1 发生故障,R1 将被提升,因为两个 Sentinel 能够就故障达成一致(显然,法定人数设置为 1),并且还可以授权故障转移,因为大多数 Sentinel 为两个。 所以表面上它可能有效,但请查看以下要点,了解为什么此设置有缺陷。
  • 如果运行 M1 的框停止工作,S1 也会停止工作。 另一个框 S2 中运行的 Sentinel 将无法授权故障转移,因此系统将不可用。

请注意,需要大多数 Sentinel 才能对不同的故障转移进行排序,并在稍后将最新的配置传播到所有 Sentinel。 还要注意,在上述设置的单侧进行故障转移的能力(无需任何协议)将非常危险

+----+           +------+
| M1 |----//-----| [M1] |
| S1 |           | S2   |
+----+           +------+

在上面的配置中,我们以完全对称的方式创建了两个主节点(假设 S2 可以在没有授权的情况下进行故障转移)。 客户端可以无限期地写入两侧,并且无法了解分区何时恢复,哪个配置是正确的,以防止出现永久性脑裂状态

因此,请始终在三个不同的框中部署至少三个 Sentinel

示例 2:三个框的基本设置

这是一个非常简单的设置,它具有易于调整以提高安全性的优势。 它基于三个框,每个框都运行一个 Redis 进程和一个 Sentinel 进程。

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

Configuration: quorum = 2

如果主节点 M1 发生故障,S2 和 S3 将就故障达成一致,并且能够授权故障转移,使客户端能够继续工作。

在每个 Sentinel 设置中,由于 Redis 使用异步复制,因此始终存在丢失一些写入的风险,因为给定的已确认写入可能无法到达被提升为主节点的从节点。 但是,在上面的设置中,由于客户端被分区,并与旧的主节点隔离,因此风险更高,如下面的图片所示

         +----+
         | M1 |
         | S1 | <- C1 (writes will be lost)
         +----+
            |
            /
            /
+------+    |    +----+
| [M2] |----+----| R3 |
| S2   |         | S3 |
+------+         +----+

在这种情况下,网络分区隔离了旧的主节点 M1,因此从节点 R2 被提升为主节点。 但是,与旧主节点位于同一分区中的客户端(如 C1)可能会继续将数据写入旧主节点。 这些数据将永远丢失,因为当分区恢复时,主节点将被重新配置为新主节点的从节点,丢弃其数据集。

可以使用以下 Redis 复制功能来缓解此问题,该功能允许主服务器在检测到它无法再将写入传输到指定数量的副本时停止接受写入。

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

使用上述配置(有关更多信息,请参阅 Redis 发行版中自注释的 redis.conf 示例),当 Redis 实例充当主服务器时,如果它无法写入至少 1 个副本,它将停止接受写入。由于复制是异步的,无法写入实际上意味着副本已断开连接,或者在超过指定的 max-lag 秒数内没有向我们发送异步确认。

使用此配置,上述示例中的旧 Redis 主服务器 M1 将在 10 秒后变为不可用。当分区恢复时,Sentinel 配置将收敛到新的配置,客户端 C1 将能够获取有效的配置并继续使用新的主服务器。

但是,天下没有免费的午餐。通过这种改进,如果两个副本都关闭,主服务器将停止接受写入。这是一个权衡。

示例 3:客户端框中的 Sentinel

有时我们只有两个可用的 Redis 框,一个用于主服务器,一个用于副本。在这种情况下,示例 2 中的配置不可行,因此我们可以采用以下方法,将 Sentinel 放置在客户端所在的位置

            +----+         +----+
            | M1 |----+----| R1 |
            |    |    |    |    |
            +----+    |    +----+
                      |
         +------------+------------+
         |            |            |
         |            |            |
      +----+        +----+      +----+
      | C1 |        | C2 |      | C3 |
      | S1 |        | S2 |      | S3 |
      +----+        +----+      +----+

      Configuration: quorum = 2

在此设置中,Sentinel 的观点与客户端相同:如果主服务器可供大多数客户端访问,则一切正常。这里的 C1、C2、C3 是通用客户端,并不意味着 C1 标识连接到 Redis 的单个客户端。它更有可能类似于应用程序服务器、Rails 应用程序或类似的东西。

如果运行 M1 和 S1 的框出现故障,故障转移将顺利进行,但是很容易看出不同的网络分区会导致不同的行为。例如,如果客户端和 Redis 服务器之间的网络断开连接,Sentinel 将无法设置,因为 Redis 主服务器和副本都将不可用。

请注意,如果 C3 与 M1 分区(在上面描述的网络中几乎不可能,但在不同的布局中更有可能,或者由于软件层中的故障),我们会遇到与示例 2 中描述的类似问题,区别在于这里我们没有办法打破对称性,因为只有一个副本和主服务器,因此主服务器在与副本断开连接时无法停止接受查询,否则主服务器在副本故障期间将永远不可用。

因此,这是一个有效的设置,但示例 2 中的设置有一些优势,例如 Redis 的 HA 系统运行在与 Redis 本身相同的框中,这可能更容易管理,并且能够限制处于少数分区中的主服务器可以接收写入的时间量。

示例 4:Sentinel 客户端端,客户端数量少于三个

如果客户端端(例如三个 Web 服务器)的框数少于三个,则无法使用示例 3 中描述的设置。在这种情况下,我们需要采用以下混合设置

            +----+         +----+
            | M1 |----+----| R1 |
            | S1 |    |    | S2 |
            +----+    |    +----+
                      |
               +------+-----+
               |            |
               |            |
            +----+        +----+
            | C1 |        | C2 |
            | S3 |        | S4 |
            +----+        +----+

      Configuration: quorum = 3

这类似于示例 3 中的设置,但这里我们在可用的四个框中运行四个 Sentinel。如果主服务器 M1 变为不可用,其他三个 Sentinel 将执行故障转移。

理论上,此设置在删除运行 C2 和 S4 的框并将其法定人数设置为 2 时仍然有效。但是,我们不太可能在没有应用程序层高可用性的情况下,在 Redis 端实现 HA。

Sentinel、Docker、NAT 和可能出现的问题

Docker 使用一种称为端口映射的技术:在 Docker 容器中运行的程序可能会使用与程序认为正在使用的端口不同的端口公开。这对于在同一台服务器上同时使用相同端口运行多个容器非常有用。

Docker 不是唯一发生这种情况的软件系统,还有其他网络地址转换设置,其中端口可能会重新映射,有时不是端口,而是 IP 地址。

重新映射端口和地址会以两种方式导致 Sentinel 出现问题

  1. Sentinel 的其他 Sentinel 自动发现不再起作用,因为它基于hello 消息,其中每个 Sentinel 都宣布其侦听连接的端口和 IP 地址。但是,Sentinel 无法理解地址或端口已重新映射,因此它宣布的信息对于其他 Sentinel 连接来说是不正确的。
  2. 副本在 Redis 主服务器的 INFO 输出中以类似的方式列出:地址由主服务器通过检查 TCP 连接的远程对等方来检测,而端口由副本本身在握手期间进行广告,但端口可能是错误的与要点 1 中介绍的原因相同。

由于 Sentinel 使用主服务器的 INFO 输出信息来自动检测副本,因此检测到的副本将无法访问,并且 Sentinel 将永远无法对主服务器进行故障转移,因为从系统的角度来看没有合适的副本,因此目前无法使用 Sentinel 监控使用 Docker 部署的主服务器和副本实例集,除非你指示 Docker 将端口 1:1 映射

对于第一个问题,如果你想使用 Docker 运行一组 Sentinel 实例,并转发端口(或任何其他端口被重新映射的 NAT 设置),可以使用以下两个 Sentinel 配置指令强制 Sentinel 宣布一组特定的 IP 和端口

sentinel announce-ip <ip>
sentinel announce-port <port>

请注意,Docker 能够在主机网络模式下运行(有关更多信息,请查看 --net=host 选项)。这应该不会出现任何问题,因为在此设置中端口不会被重新映射。

IP 地址和 DNS 名称

旧版本的 Sentinel 不支持主机名,并且要求在任何地方都指定 IP 地址。从 6.2 版开始,Sentinel 可选支持主机名。

默认情况下,此功能被禁用。如果你要启用 DNS/主机名支持,请注意

  1. Redis 和 Sentinel 节点的名称解析配置必须可靠,并且能够快速解析地址。地址解析中的意外延迟可能会对 Sentinel 产生负面影响。
  2. 你应该在所有地方都使用主机名,并避免混合使用主机名和 IP 地址。为此,请分别对所有 Redis 和 Sentinel 实例使用 replica-announce-ip <hostname>sentinel announce-ip <hostname>

启用 resolve-hostnames 全局配置允许 Sentinel 接受主机名

  • 作为 sentinel monitor 命令的一部分
  • 作为副本地址,如果副本使用 replica-announce-ip 的主机名值

Sentinel 将接受主机名作为有效输入并解析它们,但仍然在宣布实例、更新配置文件等时引用 IP 地址。

启用 announce-hostnames 全局配置使 Sentinel 使用主机名。这会影响对客户端的回复、写入配置文件的值、发送给副本的 REPLICAOF 命令等。

此行为可能与所有 Sentinel 客户端不兼容,这些客户端可能明确期望 IP 地址。

当客户端使用 TLS 连接到实例并需要名称而不是 IP 地址来执行证书 ASN 匹配时,使用主机名可能很有用。

快速教程

在本文档的后续部分,将逐步介绍有关 Sentinel API、配置和语义的所有详细信息。但是,对于想要尽快使用该系统的人员,本节是一个教程,展示如何配置和与 3 个 Sentinel 实例交互。

这里我们假设实例在端口 5000、5001、5002 上执行。我们还假设你在端口 6379 上有一个运行的 Redis 主服务器,以及在端口 6380 上运行的副本。在整个教程中,我们将使用 IPv4 环回地址 127.0.0.1,假设你在个人计算机上运行模拟。

三个 Sentinel 配置文件应如下所示

port 5000
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1

另外两个配置文件将相同,但使用 5001 和 5002 作为端口号。

关于上述配置,需要注意几点

  • 主服务器集称为 mymaster。它标识主服务器及其副本。由于每个主服务器集都有不同的名称,因此 Sentinel 可以同时监控不同的主服务器和副本集。
  • 法定人数设置为 2 的值(sentinel monitor 配置指令的最后一个参数)。
  • down-after-milliseconds 值为 5000 毫秒,即 5 秒,因此一旦我们在此时间段内没有收到来自我们的 ping 的任何回复,主服务器就会被检测为出现故障。

启动三个 Sentinel 后,你会看到它们记录的一些消息,例如

+monitor master mymaster 127.0.0.1 6379 quorum 2

这是一个 Sentinel 事件,如果你 SUBSCRIBE 到事件名称(如 发布/订阅消息部分 中所述),你可以通过发布/订阅接收此类事件。

Sentinel 在故障检测和故障转移过程中生成和记录不同的事件。

询问 Sentinel 关于主服务器的状态

要开始使用 Sentinel,最明显的事情是检查它正在监控的主服务器是否运行良好

$ redis-cli -p 5000
127.0.0.1:5000> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "6379"
 7) "runid"
 8) "953ae6a589449c13ddefaee3538d356d287f509b"
 9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "735"
19) "last-ping-reply"
20) "735"
21) "down-after-milliseconds"
22) "5000"
23) "info-refresh"
24) "126"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "532439"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "1"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "60000"
39) "parallel-syncs"
40) "1"

如你所见,它打印了关于主服务器的大量信息。其中一些对我们特别感兴趣

  1. num-other-sentinels 为 2,因此我们知道 Sentinel 已经检测到该主服务器的另外两个 Sentinel。如果你检查日志,你会看到生成的 +sentinel 事件。
  2. flags 只是 master。如果主服务器出现故障,我们预计会在此处看到 s_downo_down 标志。
  3. num-slaves 正确设置为 1,因此 Sentinel 也检测到我们的主服务器有一个附加的副本。

为了进一步了解此实例,你可能想尝试以下两个命令

SENTINEL replicas mymaster
SENTINEL sentinels mymaster

第一个将提供有关连接到主服务器的副本的类似信息,第二个将提供有关其他 Sentinel 的信息。

获取当前主服务器的地址

正如我们已经指定的那样,Sentinel 还可以充当想要连接到一组主服务器和副本的客户端的配置提供者。由于可能存在故障转移或重新配置,因此客户端不知道对于给定的实例集,当前活跃的主服务器是谁,因此 Sentinel 公开了 API 来询问此问题

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6379"

测试故障转移

此时,我们的玩具 Sentinel 部署已准备好进行测试。我们只需要杀死我们的主服务器并检查配置是否更改。为此,我们只需执行

redis-cli -p 6379 DEBUG sleep 30

此命令将使我们的主服务器不再可访问,休眠 30 秒。它基本上模拟了主服务器由于某种原因而挂起的情况。

如果你检查 Sentinel 日志,你应该能够看到很多操作

  1. 每个 Sentinel 都通过 +sdown 事件检测到主服务器已关闭。
  2. 此事件随后升级为 +odown,这意味着多个 Sentinel 同意主服务器不可访问的事实。
  3. Sentinel 投票选出一个 Sentinel 来启动第一次故障转移尝试。
  4. 故障转移发生。

如果你再次询问 mymaster 的当前主服务器地址,最终我们应该得到不同的回复

127.0.0.1:5000> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

到目前为止一切顺利... 此时,你可以跳过创建 Sentinel 部署,或者可以阅读更多内容以了解所有 Sentinel 命令和内部机制。

Sentinel API

Sentinel 提供 API 以检查其状态,检查监控的主服务器和副本的运行状况,订阅以接收特定通知以及在运行时更改 Sentinel 配置。

默认情况下,Sentinel 使用 TCP 端口 26379 运行(注意 6379 是正常的 Redis 端口)。Sentinel 接受使用 Redis 协议的命令,因此您可以使用 `redis-cli` 或任何其他未修改的 Redis 客户端与 Sentinel 交谈。

可以直接查询 Sentinel 来查看它所监控的 Redis 实例的状态,查看它知道的其他 Sentinel 等等。或者,使用 Pub/Sub,可以从 Sentinel 接收“推送式”通知,每次发生某些事件时,例如故障转移,或实例进入错误状态等等。

Sentinel 命令

`SENTINEL` 命令是 Sentinel 的主要 API。以下是其子命令列表(适用于适用的最小版本)

  • **SENTINEL CONFIG GET `<name>`** ( `>= 6.2` ) 获取全局 Sentinel 配置参数的当前值。指定名称可以是通配符,类似于 Redis 的 `CONFIG GET` 命令。
  • **SENTINEL CONFIG SET `<name>` `<value>`** ( `>= 6.2` ) 设置全局 Sentinel 配置参数的值。
  • **SENTINEL CKQUORUM `<master name>`** 检查当前 Sentinel 配置是否能够达到故障转移主服务器所需的仲裁,以及授权故障转移所需的多数。此命令应在监控系统中使用,以检查 Sentinel 部署是否正常。
  • **SENTINEL FLUSHCONFIG** 强制 Sentinel 将其配置重写到磁盘,包括当前 Sentinel 状态。通常,Sentinel 每次在其状态发生变化时都会重写配置(在跨重启持久化到磁盘的状态子集的上下文中)。但是,有时配置文件可能会由于操作错误、磁盘故障、软件包升级脚本或配置管理器而丢失。在这种情况下,强制 Sentinel 重写配置文件的方法非常有用。即使之前的配置文件完全丢失,此命令也能正常工作。
  • **SENTINEL FAILOVER `<master name>`** 强制进行故障转移,就像主服务器不可访问一样,并且无需向其他 Sentinel 请求同意(但是,将发布新版本的配置,以便其他 Sentinel 更新其配置)。
  • **SENTINEL GET-MASTER-ADDR-BY-NAME `<master name>`** 返回具有该名称的主服务器的 IP 和端口号。如果此主服务器正在进行故障转移或已成功终止故障转移,则返回已提升副本的地址和端口。
  • **SENTINEL INFO-CACHE** ( `>= 3.2` ) 返回来自主服务器和副本的缓存 `INFO` 输出。
  • **SENTINEL IS-MASTER-DOWN-BY-ADDR ** 检查从当前 Sentinel 的角度来看,由 ip:port 指定的主服务器是否已关闭。此命令主要用于内部使用。
  • **SENTINEL MASTER `<master name>`** 显示指定主服务器的状态和信息。
  • **SENTINEL MASTERS** 显示已监控主服务器及其状态的列表。
  • **SENTINEL MONITOR** 启动 Sentinel 的监控。有关更多信息,请参阅 在运行时重新配置 Sentinel 部分
  • **SENTINEL MYID** ( `>= 6.2` ) 返回 Sentinel 实例的 ID。
  • **SENTINEL PENDING-SCRIPTS** 此命令返回有关挂起脚本的信息。
  • **SENTINEL REMOVE** 停止 Sentinel 的监控。有关更多信息,请参阅 在运行时重新配置 Sentinel 部分
  • **SENTINEL REPLICAS `<master name>`** ( `>= 5.0` ) 显示此主服务器的副本及其状态的列表。
  • **SENTINEL SENTINELS `<master name>`** 显示此主服务器的 Sentinel 实例及其状态的列表。
  • **SENTINEL SET** 设置 Sentinel 的监控配置。有关更多信息,请参阅 在运行时重新配置 Sentinel 部分
  • **SENTINEL SIMULATE-FAILURE (crash-after-election|crash-after-promotion|help)** ( `>= 3.2` ) 此命令模拟不同的 Sentinel 崩溃场景。
  • **SENTINEL RESET `<pattern>`** 此命令将重置所有名称匹配的主服务器。模式参数是 glob 风格的模式。重置过程将清除主服务器中的任何先前状态(包括正在进行的故障转移),并删除已发现并与主服务器关联的每个副本和 Sentinel。

出于连接管理和管理目的,Sentinel 支持 Redis 命令的以下子集

  • **ACL** ( `>= 6.2` ) 此命令管理 Sentinel 访问控制列表。有关更多信息,请参阅 ACL 文档页面和 Sentinel 访问控制列表身份验证
  • **AUTH** ( `>= 5.0.1` ) 身份验证客户端连接。有关更多信息,请参阅 `AUTH` 命令和 使用身份验证配置 Sentinel 实例 部分
  • **CLIENT** 此命令管理客户端连接。有关更多信息,请参阅其子命令页面。
  • **COMMAND** ( `>= 6.2` ) 此命令返回有关命令的信息。有关更多信息,请参阅 `COMMAND` 命令及其各种子命令。
  • **HELLO** ( `>= 6.0` ) 切换连接的协议。有关更多信息,请参阅 `HELLO` 命令。
  • **INFO** 返回有关 Sentinel 服务器的信息和统计信息。有关更多信息,请参阅 `INFO` 命令。
  • **PING** 此命令只返回 PONG。
  • **ROLE** 此命令返回字符串“sentinel”和已监控主服务器的列表。有关更多信息,请参阅 `ROLE` 命令。
  • **SHUTDOWN** 关闭 Sentinel 实例。

最后,Sentinel 还支持 `SUBSCRIBE``UNSUBSCRIBE``PSUBSCRIBE``PUNSUBSCRIBE` 命令。有关更多详细信息,请参阅 Pub/Sub 消息 部分

在运行时重新配置 Sentinel

从 Redis 版本 2.8.4 开始,Sentinel 提供了一个 API,用于添加、删除或更改给定主服务器的配置。请注意,如果您有多个 Sentinel,则应将更改应用到所有实例,以使 Redis Sentinel 正确运行。这意味着更改单个 Sentinel 的配置不会自动将更改传播到网络中的其他 Sentinel。

以下是在更新 Sentinel 实例的配置时使用的一系列 `SENTINEL` 子命令。

  • **SENTINEL MONITOR `<name>` `<ip>` `<port>` `<quorum>`** 此命令告诉 Sentinel 开始监控具有指定名称、IP、端口和仲裁的新主服务器。它与 `sentinel.conf` 配置文件中的 `sentinel monitor` 配置指令相同,不同之处在于您不能使用主机名作为 `ip`,而需要提供 IPv4 或 IPv6 地址。
  • **SENTINEL REMOVE `<name>`** 用于删除指定的主服务器:主服务器将不再被监控,并且将从 Sentinel 的内部状态中完全删除,因此它将不再列在 `SENTINEL masters` 中等等。
  • **SENTINEL SET `<name>` [ `<option>` `<value>` ...]** SET 命令与 Redis 的 `CONFIG SET` 命令非常相似,用于更改特定主服务器的配置参数。可以指定多个选项/值对(或根本不指定)。可以通过 `sentinel.conf` 配置的所有配置参数也可以使用 SET 命令配置。

以下是如何使用 `SENTINEL SET` 命令修改名为 `objects-cache` 的主服务器的 `down-after-milliseconds` 配置的示例

SENTINEL SET objects-cache-master down-after-milliseconds 1000

如前所述,`SENTINEL SET` 可用于设置启动配置文件中可设置的所有配置参数。此外,还可以只更改主服务器仲裁配置,而无需使用 `SENTINEL REMOVE` 后跟 `SENTINEL MONITOR` 删除并重新添加主服务器,而只需使用

SENTINEL SET objects-cache-master quorum 5

请注意,没有等效的 GET 命令,因为 `SENTINEL MASTER` 以易于解析的格式(作为字段/值对数组)提供所有配置参数。

从 Redis 版本 6.2 开始,Sentinel 还允许获取和设置全局配置参数,这些参数以前只在版本 6.2 之前的配置文件中受支持。

  • **SENTINEL CONFIG GET `<name>`** 获取全局 Sentinel 配置参数的当前值。指定名称可以是通配符,类似于 Redis 的 `CONFIG GET` 命令。
  • **SENTINEL CONFIG SET `<name>` `<value>`** 设置全局 Sentinel 配置参数的值。

可以操作的全局参数包括

添加或删除 Sentinel

由于 Sentinel 实现的自动发现机制,将新 Sentinel 添加到部署中是一个简单的过程。您只需启动配置为监控当前活动主服务器的新 Sentinel。在 10 秒内,Sentinel 将获取其他 Sentinel 的列表以及连接到主服务器的副本集。

如果您需要一次添加多个 Sentinel,建议您逐个添加它们,等待所有其他 Sentinel 了解第一个 Sentinel,然后再添加下一个。这对于确保多数只能在分区的一侧实现非常有用,以防在添加新 Sentinel 的过程中发生故障。

这可以通过每隔 30 秒添加一个新 Sentinel 来轻松实现,并且在没有网络分区的情况下进行操作。

在过程结束时,可以使用命令 `SENTINEL MASTER mastername` 来检查所有 Sentinel 是否同意监控主服务器的 Sentinel 总数。

删除 Sentinel 稍微复杂一些:**Sentinel 永远不会忘记已看到的 Sentinel**,即使它们长时间无法访问,因为我们不想动态更改授权故障转移和创建新配置编号所需的多数。因此,为了删除 Sentinel,应在没有网络分区的情况下执行以下步骤

  1. 停止要删除的 Sentinel 的 Sentinel 进程。
  2. 向所有其他 Sentinel 实例发送 `SENTINEL RESET *` 命令(您可以使用确切的主服务器名称代替 `*`,如果您只想重置单个主服务器)。逐个执行,每次执行之间至少等待 30 秒。
  3. 通过检查每个 Sentinel 的 `SENTINEL MASTER mastername` 的输出,检查所有 Sentinel 是否同意当前活动的 Sentinel 数。

删除旧的主服务器或不可访问的副本

哨兵永远不会忘记给定主服务器的副本,即使它们长时间无法访问。这很有用,因为哨兵应该能够在网络分区或故障事件后正确地重新配置返回的副本。

此外,在故障转移后,失败的主服务器将虚拟地添加为新主服务器的副本,这样它将在再次可用后重新配置为与新主服务器复制。

但是,有时您可能想要永久地从哨兵监控的副本列表中删除一个副本(它可能是旧的主服务器)。

为此,您需要向所有哨兵发送 SENTINEL RESET mastername 命令:它们将在接下来的 10 秒内刷新副本列表,只添加从当前主服务器正确复制的副本,如 INFO 输出所示。

发布/订阅消息

客户端可以使用哨兵作为 Redis 兼容的发布/订阅服务器(但您不能使用 PUBLISH)来 SUBSCRIBEPSUBSCRIBE 到频道,并接收有关特定事件的通知。

频道名称与事件名称相同。例如,名为 +sdown 的频道将接收所有与实例进入 SDOWN(SDOWN 表示从您查询的哨兵的角度来看,该实例不再可访问)状态相关的通知。

要获取所有消息,只需使用 PSUBSCRIBE * 订阅。

以下是您可以使用此 API 接收的频道和消息格式列表。第一个词是频道/事件名称,其余的是数据的格式。

注意:如果指定了实例详细信息,则表示提供以下参数来标识目标实例

<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>

标识主服务器的部分(从 @ 参数到结尾)是可选的,只有在实例本身不是主服务器时才会指定。

  • +reset-master <instance details> -- 主服务器已重置。
  • +slave <instance details> -- 检测到并附加了一个新的副本。
  • +failover-state-reconf-slaves <instance details> -- 故障转移状态更改为 reconf-slaves 状态。
  • +failover-detected <instance details> -- 检测到由另一个哨兵或任何其他外部实体启动的故障转移(附加的副本变成了主服务器)。
  • +slave-reconf-sent <instance details> -- 主导哨兵发送了 REPLICAOF 命令到该实例,以便为新的副本重新配置它。
  • +slave-reconf-inprog <instance details> -- 被重新配置的副本显示为新主服务器 ip:port 对的副本,但同步过程尚未完成。
  • +slave-reconf-done <instance details> -- 该副本现在已与新主服务器同步。
  • -dup-sentinel <instance details> -- 指定主服务器的一个或多个哨兵被移除,因为它们是重复的(例如,当哨兵实例重启时会发生这种情况)。
  • +sentinel <instance details> -- 检测到并附加了该主服务器的一个新的哨兵。
  • +sdown <instance details> -- 指定的实例现在处于主观停机状态。
  • -sdown <instance details> -- 指定的实例不再处于主观停机状态。
  • +odown <instance details> -- 指定的实例现在处于客观停机状态。
  • -odown <instance details> -- 指定的实例不再处于客观停机状态。
  • +new-epoch <instance details> -- 当前纪元已更新。
  • +try-failover <instance details> -- 新的故障转移正在进行中,正在等待多数派选举。
  • +elected-leader <instance details> -- 赢得了指定纪元的选举,可以执行故障转移。
  • +failover-state-select-slave <instance details> -- 新的故障转移状态是 select-slave:我们正在尝试找到适合提升的副本。
  • no-good-slave <instance details> -- 没有合适的副本可以提升。目前我们将在一段时间后尝试,但可能这会改变,在这种情况下,状态机将完全中止故障转移。
  • selected-slave <instance details> -- 我们找到了指定的合适副本进行提升。
  • failover-state-send-slaveof-noone <instance details> -- 我们正在尝试将提升的副本重新配置为主服务器,等待它切换。
  • failover-end-for-timeout <instance details> -- 由于超时,故障转移终止,副本最终将被配置为与新主服务器复制。
  • failover-end <instance details> -- 故障转移成功终止。所有副本似乎都已重新配置为与新主服务器复制。
  • switch-master <master name> <oldip> <oldport> <newip> <newport> -- 配置更改后,主服务器的新 IP 和地址为指定地址。这对于大多数外部用户来说是最感兴趣的消息
  • +tilt -- 进入倾斜模式。
  • -tilt -- 退出倾斜模式。

处理 -BUSY 状态

当 Lua 脚本运行时间超过配置的 Lua 脚本时间限制时,Redis 实例将返回 -BUSY 错误。当这发生在触发故障转移之前,Redis 哨兵将尝试发送 SCRIPT KILL 命令,该命令只有在脚本是只读的时才会成功。

如果实例在此尝试后仍然处于错误状态,它最终将被故障转移。

副本优先级

Redis 实例有一个名为 replica-priority 的配置参数。此信息由 Redis 副本实例在它们的 INFO 输出中公开,哨兵使用它来从可以用于故障转移主服务器的副本中选择一个副本。

  1. 如果副本优先级设置为 0,则副本永远不会被提升为主服务器。
  2. 具有较低优先级编号的副本优先于哨兵。

例如,如果当前主服务器的数据中心有一个副本 S1,另一个数据中心有一个副本 S2,则可以将 S1 的优先级设置为 10,将 S2 的优先级设置为 100,这样如果主服务器出现故障,并且 S1 和 S2 都可用,S1 将优先于 S2。

有关选择副本方式的更多信息,请查看本文档的 副本选择和优先级部分

哨兵和 Redis 身份验证

当主服务器配置为要求来自客户端的身份验证时,作为安全措施,副本也需要知道凭据才能向主服务器进行身份验证,并创建用于异步复制协议的主服务器-副本连接。

Redis 访问控制列表身份验证

从 Redis 6 开始,用户身份验证和权限由 访问控制列表 (ACL) 管理。

为了让哨兵在使用 ACL 配置 Redis 服务器实例时连接到这些实例,哨兵配置必须包含以下指令

sentinel auth-user <master-name> <username>
sentinel auth-pass <master-name> <password>

其中 <username><password> 是访问组实例的用户名和密码。这些凭据应该在组的所有 Redis 实例上配置,并具有最小的控制权限。例如

127.0.0.1:6379> ACL SETUSER sentinel-user ON >somepassword allchannels +multi +slaveof +ping +exec +subscribe +config|rewrite +role +publish +info +client|setname +client|kill +script|kill

Redis 密码身份验证

在 Redis 6 之前,身份验证是通过以下配置指令实现的

  • requirepass 在主服务器中,用于设置身份验证密码,并确保实例不会处理非身份验证客户端的请求。
  • masterauth 在副本中,以便副本向主服务器进行身份验证,以便从主服务器正确复制数据。

当使用哨兵时,没有单个主服务器,因为在故障转移后,副本可能充当主服务器的角色,而旧的主服务器可以重新配置为充当副本,因此您要做的就是在所有实例(主服务器和副本)中设置上述指令。

这通常也是一个合理的设置,因为您不希望只在主服务器中保护数据,而让相同的数据在副本中可访问。

但是,在您需要一个无需身份验证即可访问的副本的罕见情况下,您仍然可以通过设置副本优先级为零来做到这一点,以防止该副本被提升为主服务器,并在该副本中仅配置 masterauth 指令,而不使用 requirepass 指令,这样数据就可以被未经身份验证的客户端读取。

为了让哨兵在使用 requirepass 配置 Redis 服务器实例时连接到这些实例,哨兵配置必须包含 sentinel auth-pass 指令,格式如下

sentinel auth-pass <master-name> <password>

使用身份验证配置哨兵实例

哨兵实例本身可以通过要求客户端通过 AUTH 命令进行身份验证来保护。从 Redis 6.2 开始,访问控制列表 (ACL) 可用,而以前版本(从 Redis 5.0.1 开始)支持仅密码身份验证。

请注意,哨兵的身份验证配置应应用于部署中的每个实例,并且所有实例应使用相同的配置。此外,ACL 和仅密码身份验证不应该一起使用。

哨兵访问控制列表身份验证

使用 ACL 保护哨兵实例的第一步是阻止任何未经授权的访问。为此,您需要禁用默认超级用户(或者至少为其设置一个强密码),并创建一个新的超级用户,并允许其访问发布/订阅频道

127.0.0.1:5000> ACL SETUSER admin ON >admin-password allchannels +@all
OK
127.0.0.1:5000> ACL SETUSER default off
OK

默认用户由哨兵用于连接到其他实例。您可以使用以下配置指令提供另一个超级用户的凭据

sentinel sentinel-user <username>
sentinel sentinel-pass <password>

其中 <username><password> 分别是哨兵的超级用户和密码(例如,上面示例中的 adminadmin-password)。

最后,为了对传入的客户端连接进行身份验证,您可以创建如下所示的哨兵受限用户配置文件

127.0.0.1:5000> ACL SETUSER sentinel-user ON >user-password -@all +auth +client|getname +client|id +client|setname +command +hello +ping +role +sentinel|get-master-addr-by-name +sentinel|master +sentinel|myid +sentinel|replicas +sentinel|sentinels +sentinel|masters

有关更多信息,请参考您选择的哨兵客户端的文档。

哨兵仅密码身份验证

要将哨兵与仅密码身份验证一起使用,请将 requirepass 配置指令添加到所有哨兵实例中,如下所示

requirepass "your_password_here"

以这种方式配置后,哨兵将执行两件事

  1. 客户端将需要密码才能向哨兵发送命令。这很明显,因为这就是此类配置指令在 Redis 中的一般工作方式。
  2. 此外,用于访问本地哨兵的相同密码将由该哨兵实例用于向其连接的所有其他哨兵实例进行身份验证。

这意味着您必须在所有哨兵实例中配置相同的 requirepass 密码。这样,每个哨兵都可以与其他每个哨兵通信,而无需为每个哨兵配置访问所有其他哨兵的密码,这样非常不实用。

在使用此配置之前,请确保您的客户端库可以向哨兵实例发送 AUTH 命令。

哨兵客户端实现


哨兵需要显式客户端支持,除非系统配置为执行一个脚本,该脚本将所有请求透明地重定向到新的主服务器实例(虚拟 IP 或其他类似系统)。客户端库实现主题在文档 哨兵客户端指南 中介绍。

更高级的概念

在下文中,我们将介绍一些关于 Sentinel 工作原理的细节,但不会涉及实现细节和算法,这些内容将在本文档的最后一部分介绍。

SDOWN 和 ODOWN 故障状态

Redis Sentinel 有两种不同的“宕机”概念,一种称为“主观宕机”条件(SDOWN),是特定 Sentinel 实例的本地宕机条件。另一种称为“客观宕机”条件(ODOWN),当足够多的 Sentinel(至少与监控主服务器的 quorum 参数配置的数量一样多)处于 SDOWN 状态,并使用 SENTINEL is-master-down-by-addr 命令从其他 Sentinel 获取反馈时,就会达到 ODOWN 状态。

从 Sentinel 的角度来看,当它在配置中指定的 is-master-down-after-milliseconds 参数所确定的时间段内,未收到对 PING 请求的有效回复时,就会达到 SDOWN 状态。

对 PING 的有效回复包括以下几种:

  • PING 回复 +PONG。
  • PING 回复 -LOADING 错误。
  • PING 回复 -MASTERDOWN 错误。

任何其他回复(或根本没有回复)都被视为无效回复。但是请注意,**逻辑主服务器在 INFO 输出中将自己标识为从服务器,则被视为已宕机**。

请注意,SDOWN 要求在配置的整个时间段内未收到任何有效回复。例如,如果时间段为 30000 毫秒(30 秒),并且每 29 秒收到一个有效的 PING 回复,则该实例被视为正常工作。

SDOWN 不足以触发故障转移:它只表示单个 Sentinel 认为 Redis 实例不可用。要触发故障转移,必须达到 ODOWN 状态。

从 SDOWN 切换到 ODOWN 不会使用任何强一致性算法,而只是一种流言蜚语形式:如果特定 Sentinel 在**给定时间范围内**从足够多的 Sentinel 收到有关主服务器故障的报告,则 SDOWN 将提升到 ODOWN。如果稍后缺少此确认,则此标志将被清除。

要真正开始故障转移,需要使用实际上的多数票的更严格的授权,但如果没有达到 ODOWN 状态,就不会触发任何故障转移。

ODOWN 条件**仅适用于主服务器**。对于其他类型的实例,Sentinel 不需要采取任何行动,因此从服务器和其他 Sentinel 永远不会达到 ODOWN 状态,只会达到 SDOWN 状态。

但是,SDOWN 也有语义含义。例如,处于 SDOWN 状态的从服务器不会被执行故障转移的 Sentinel 选为提升对象。

Sentinel 和从服务器的自动发现

Sentinel 保持与其他 Sentinel 的连接,以便相互检查彼此的可用性,并交换消息。但是,您不需要在运行的每个 Sentinel 实例中配置其他 Sentinel 地址列表,因为 Sentinel 使用 Redis 实例的发布/订阅功能来发现监控相同主服务器和从服务器的其他 Sentinel。

此功能通过将问候消息发送到名为 __sentinel__:hello 的频道来实现。

类似地,您不需要配置附加到主服务器的从服务器列表,因为 Sentinel 会自动发现此列表,并查询 Redis。

  • 每个 Sentinel 每两秒钟会向监控的每个主服务器和从服务器的发布/订阅频道 __sentinel__:hello 发布一条消息,用 IP 地址、端口和运行 ID 宣布其存在。
  • 每个 Sentinel 都订阅了每个主服务器和从服务器的发布/订阅频道 __sentinel__:hello,查找未知的 Sentinel。当检测到新的 Sentinel 时,它们将被添加为主服务器的 Sentinel。
  • 问候消息还包含主服务器的完整当前配置。如果接收到的 Sentinel 的主服务器配置比收到的配置旧,则它会立即更新为新配置。
  • 在将新的 Sentinel 添加到主服务器之前,Sentinel 始终会检查是否已经存在运行 ID 或地址(IP 地址和端口对)相同的 Sentinel。如果是,则会删除所有匹配的 Sentinel,并添加新的 Sentinel。

在故障转移过程之外重新配置 Sentinel 实例

即使没有进行故障转移,Sentinel 也会始终尝试在监控的实例上设置当前配置。具体而言,

  • 根据当前配置,声称自己是主服务器的从服务器将被配置为从服务器,以复制当前主服务器。
  • 连接到错误主服务器的从服务器将被重新配置为复制正确的主服务器。

为了让 Sentinel 重新配置从服务器,必须观察到一段时间内的错误配置,该时间段大于用于广播新配置的时间段。

这将防止具有陈旧配置的 Sentinel(例如,由于刚从分区中重新加入)尝试在收到更新之前更改从服务器的配置。

还要注意,始终尝试强制执行当前配置的语义使故障转移对分区更具抵抗力。

  • 故障转移的主服务器在恢复可用时将被重新配置为从服务器。
  • 在分区期间被分区隔离的从服务器在恢复可达后将被重新配置。

关于本节要记住的重要教训是:**Sentinel 是一个系统,其中每个进程都会始终尝试将最新的逻辑配置强制执行到监控的实例集上**。

从服务器选择和优先级

当 Sentinel 实例准备好执行故障转移时,由于主服务器处于 ODOWN 状态,并且 Sentinel 收到了来自已知大多数 Sentinel 实例的故障转移授权,因此需要选择合适的从服务器。

从服务器选择过程会评估从服务器的以下信息:

  1. 与主服务器断开连接的时间。
  2. 从服务器优先级。
  3. 处理的复制偏移量。
  4. 运行 ID。

如果发现从服务器与主服务器断开连接的时间超过配置的主服务器超时时间的十倍(down-after-milliseconds 选项),加上主服务器从执行故障转移的 Sentinel 的角度来看也不可用的时间,则该从服务器被视为不适合故障转移,并将被跳过。

更准确地说,如果从服务器的 INFO 输出表明它与主服务器断开连接的时间超过

(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state

则该从服务器被视为不可靠,将被完全忽略。

从服务器选择只考虑通过上述测试的从服务器,并根据上述标准对其进行排序,排序顺序如下。

  1. 从服务器根据 Redis 实例的 redis.conf 文件中配置的 replica-priority 进行排序。较低的优先级将优先选择。
  2. 如果优先级相同,则检查从服务器处理的复制偏移量,并选择从主服务器接收更多数据的从服务器。
  3. 如果多个从服务器具有相同的优先级,并且处理了来自主服务器的相同数据,则执行进一步检查,选择具有字典序较小的运行 ID 的从服务器。具有较低的运行 ID 对于从服务器来说并不真正有利,但对于使从服务器选择过程更具确定性很有用,而不是选择随机的从服务器。

在大多数情况下,replica-priority 不需要显式设置,因此所有实例将使用相同的默认值。如果有特定的故障转移偏好,则必须在所有实例(包括主服务器)上设置 replica-priority,因为主服务器将来可能成为从服务器 - 此时它需要适当的 replica-priority 设置。

可以将 Redis 实例配置为具有 0 的特殊 replica-priority,以便 Sentinel 永远不会选择它作为新的主服务器。但是,以这种方式配置的从服务器仍然会被 Sentinel 重新配置,以便在故障转移后复制新的主服务器,唯一的区别是它本身永远不会成为主服务器。

算法和内部机制

在下文中,我们将探讨 Sentinel 行为的细节。用户不需要了解所有细节,但深入了解 Sentinel 可能有助于更有效地部署和操作 Sentinel。

仲裁

前面的部分表明,每个由 Sentinel 监控的主服务器都与一个配置的**仲裁**相关联。它指定了触发故障转移所需的 Sentinel 进程数量,这些进程需要就主服务器的不可达性或错误条件达成一致。

但是,在触发故障转移之后,为了真正执行故障转移,**至少需要大多数 Sentinel 授权 Sentinel 进行故障转移**。Sentinel 永远不会在少数 Sentinel 存在的分区中执行故障转移。

让我们尝试更清晰地说明。

  • 仲裁:检测到错误条件所需的 Sentinel 进程数量,以使主服务器被标记为**ODOWN**。
  • **ODOWN** 状态会触发故障转移。
  • 一旦触发故障转移,尝试进行故障转移的 Sentinel 必须向大多数 Sentinel 请求授权(如果仲裁设置为大于大多数票数的值,则需要超过大多数票数)。

区别可能看似细微,但实际上很容易理解和使用。例如,如果您有 5 个 Sentinel 实例,并且仲裁设置为 2,则只要 2 个 Sentinel 认为主服务器不可达,就会触发故障转移,但是这两个 Sentinel 中的一个只有在获得至少 3 个 Sentinel 的授权后才能进行故障转移。

相反,如果将仲裁配置为 5,则所有 Sentinel 都必须就主服务器错误条件达成一致,并且需要所有 Sentinel 的授权才能进行故障转移。

这意味着可以使用仲裁以两种方式调整 Sentinel:

  1. 如果将仲裁设置为小于部署的 Sentinel 多数票数的值,则实际上是在使 Sentinel 对主服务器故障更加敏感,只要少数 Sentinel 无法与主服务器通信,就会触发故障转移。
  2. 如果将仲裁设置为大于 Sentinel 多数票数的值,则实际上是在使 Sentinel 能够仅在大量(大于多数)连接良好的 Sentinel 就主服务器宕机达成一致时才进行故障转移。

配置纪元

Sentinel 需要获得大多数票数的授权才能开始故障转移,原因如下:

当 Sentinel 获得授权时,它会为它要进行故障转移的主服务器获取一个唯一的**配置纪元**。这是一个数字,将在故障转移完成后用于对新配置进行版本控制。因为大多数票数都同意将特定版本分配给特定 Sentinel,因此其他 Sentinel 将无法使用该版本。这意味着每次故障转移的配置都使用唯一的版本进行版本控制。我们将会看到为什么这一点如此重要。

此外,Sentinel 有一条规则:如果 Sentinel 为特定主服务器的故障转移投票支持另一个 Sentinel,它将等待一段时间,然后再尝试再次为同一主服务器执行故障转移。此延迟是您可以在 sentinel.conf 中配置的 2 * failover-timeout。这意味着 Sentinel 不会尝试同时为同一主服务器执行故障转移,第一个请求授权的 Sentinel 会尝试,如果失败,另一个 Sentinel 会在一段时间后尝试,依此类推。

Redis Sentinel 保证活跃性属性,如果大多数 Sentinel 能够通信,则如果主服务器已宕机,最终将会有一个 Sentinel 获得授权进行故障转移。

Redis Sentinel 还保证安全性属性,每个 Sentinel 将使用不同的配置纪元为同一主服务器执行故障转移。

配置传播

一旦 Sentinel 能够成功地为主服务器执行故障转移,它就会开始广播新配置,以便其他 Sentinel 更新其有关特定主服务器的信息。

为了使故障转移被认为成功,它需要 Sentinel 能够将REPLICAOF NO ONE命令发送到选定的副本,并且后来在主节点的INFO输出中观察到切换为主节点。

此时,即使副本的重新配置正在进行,故障转移也被认为是成功的,并且所有 Sentinel 都需要开始报告新的配置。

新的配置传播的方式是我们需要每个 Sentinel 故障转移都用不同的版本号(配置纪元)授权的原因。

每个 Sentinel 不断地广播它对主节点的配置版本,使用 Redis 发布/订阅消息,在主节点和所有副本上。同时,所有 Sentinel 都等待消息,以查看其他 Sentinel 公布的配置是什么。

配置在__sentinel__:hello发布/订阅频道中广播。

因为每个配置都有不同的版本号,所以更大的版本总是胜过较小的版本。

例如,主节点mymaster的配置从所有 Sentinel 都相信主节点在 192.168.1.50:6379 开始。此配置的版本为 1。一段时间后,Sentinel 被授权使用版本 2 进行故障转移。如果故障转移成功,它将开始广播一个新的配置,比如 192.168.1.50:9000,版本为 2。所有其他实例将看到此配置,并将相应地更新其配置,因为新配置具有更高的版本。

这意味着 Sentinel 保证了第二个活性属性:能够通信的一组 Sentinel 将全部收敛到具有更高版本号的相同配置。

基本上,如果网络被分区,每个分区将收敛到更高的本地配置。在没有分区的特殊情况下,只有一个分区,并且每个 Sentinel 将就配置达成一致。

分区一致性

Redis Sentinel 配置最终一致,因此每个分区将收敛到可用的更高配置。但是,在使用 Sentinel 的现实世界系统中,有三个不同的参与者

  • Redis 实例。
  • Sentinel 实例。
  • 客户端。

为了定义系统的行为,我们必须考虑所有三个。

以下是包含 3 个节点的简单网络,每个节点都运行一个 Redis 实例和一个 Sentinel 实例

            +-------------+
            | Sentinel 1  |----- Client A
            | Redis 1 (M) |
            +-------------+
                    |
                    |
+-------------+     |          +------------+
| Sentinel 2  |-----+-- // ----| Sentinel 3 |----- Client B
| Redis 2 (S) |                | Redis 3 (M)|
+-------------+                +------------+

在这个系统中,原始状态是 Redis 3 是主节点,而 Redis 1 和 2 是副本。分区发生了,隔离了旧的主节点。Sentinel 1 和 2 启动了故障转移,将 Sentinel 1 提升为主节点。

Sentinel 属性保证 Sentinel 1 和 2 现在具有主节点的新配置。但是 Sentinel 3 仍然具有旧的配置,因为它位于不同的分区中。

我们知道,当网络分区恢复时,Sentinel 3 将获得其配置更新,但是如果在分区期间有与旧主节点分区的客户端会发生什么?

客户端仍然可以写入旧主节点 Redis 3。当分区重新加入时,Redis 3 将变为 Redis 1 的副本,并且在分区期间写入的所有数据都将丢失。

根据您的配置,您可能希望或不希望这种情况发生

  • 如果您使用 Redis 作为缓存,即使客户端 B 的数据将丢失,它仍然能够写入旧主节点,这可能很方便。
  • 如果您使用 Redis 作为存储,这不是好事,您需要配置系统以部分防止此问题。

由于 Redis 是异步复制的,因此无法完全防止此场景中的数据丢失,但是您可以使用以下 Redis 配置选项限制 Redis 3 和 Redis 1 之间的差异

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

使用上述配置(有关更多信息,请参阅 Redis 发行版中自注释的 redis.conf 示例),当 Redis 实例充当主服务器时,如果它无法写入至少 1 个副本,它将停止接受写入。由于复制是异步的,无法写入实际上意味着副本已断开连接,或者在超过指定的 max-lag 秒数内没有向我们发送异步确认。

使用此配置,上面的示例中的 Redis 3 将在 10 秒后变为不可用。当分区恢复时,Sentinel 3 配置将收敛到新的配置,并且客户端 B 将能够获取有效的配置并继续。

总的来说,Redis + Sentinel 作为一个整体是一个最终一致系统,其中合并函数是最后一次故障转移获胜,并且来自旧主节点的数据被丢弃以复制当前主节点的数据,因此始终存在丢失已确认写入的窗口。这是由于 Redis 异步复制和系统“虚拟”合并函数的丢弃性质造成的。请注意,这不是 Sentinel 本身的限制,如果您使用强一致复制状态机编排故障转移,则相同的属性仍然适用。只有两种方法可以避免丢失已确认写入

  1. 使用同步复制(以及适当的共识算法来运行复制状态机)。
  2. 使用最终一致系统,其中相同对象的不同版本可以合并。

Redis 目前无法使用上述任何系统,并且目前不在开发目标中。但是,有一些代理在 Redis 存储之上实现了解决方案“2”,例如 SoundCloud 的Roshi,或 Netflix 的Dynomite

Sentinel 持久状态

Sentinel 状态保存在 sentinel 配置文件中。例如,每次为主节点接收或创建(领导者 Sentinel)新配置时,配置都会与配置纪元一起持久化到磁盘。这意味着停止和重新启动 Sentinel 进程是安全的。

TILT 模式

Redis Sentinel 极度依赖计算机时间:例如,为了了解实例是否可用,它会记住对 PING 命令的最后一次成功回复的时间,并将其与当前时间进行比较,以了解其时间有多久。

但是,如果计算机时间以意外的方式发生变化,或者如果计算机非常繁忙,或者进程因某种原因而阻塞,Sentinel 可能会开始以意外的方式运行。

TILT 模式是一种特殊的“保护”模式,当检测到可能降低系统可靠性的奇怪情况时,Sentinel 可以进入该模式。Sentinel 定时器中断通常每秒调用 10 次,因此我们预计两次调用定时器中断之间大约会经过 100 毫秒。

Sentinel 所做的是注册定时器中断上次被调用的时间,并将其与当前调用进行比较:如果时间差为负或意外地很大(2 秒或更长),则进入 TILT 模式(或者如果已经进入,则延迟退出 TILT 模式)。

处于 TILT 模式时,Sentinel 将继续监控一切,但是

  • 它完全停止行动。
  • 它开始对SENTINEL is-master-down-by-addr请求回复否定,因为它不再信任检测故障的能力。

如果一切都正常运行 30 秒,则退出 TILT 模式。

在 Sentinel TILT 模式下,如果我们发送 INFO 命令,我们会收到以下回复

$ redis-cli -p 26379
127.0.0.1:26379> info
(Other information from Sentinel server skipped.)

# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=0,sentinels=1

字段“sentinel_tilt_since_seconds”指示 Sentinel 处于 TILT 模式多长时间。如果它没有处于 TILT 模式,则值为 -1。

请注意,在某些方面,TILT 模式可以使用许多内核提供的单调时钟 API 来代替。但是,目前还不清楚这是否是一个好的解决方案,因为当前系统在进程暂停或长期未由调度程序执行的情况下避免了问题。

关于此手册页中使用“slave”一词的说明:从 Redis 5 开始,如果不是为了向后兼容,Redis 项目不再使用“slave”一词。不幸的是,在这个命令中,“slave”是协议的一部分,因此只有在自然弃用此 API 时,我们才能删除此类出现。

RATE THIS PAGE
Back to top ↑