sentinel 介绍

什么是 sentinel?

Redis-Sentinel 是 Redis 官方推荐的高可用性 (HA) 解决方案,当用 Redis 做 Master-slave 的高可用方案时,假如 master 宕机了,Redis 本身 (包括它的很多客户端) 都没有实现自动进行主备切换,而 Redis-sentinel 本身也是一个独立运行的进程,它能监控多个 master-slave 集群,发现 master 宕机后能进行自动切换。

sentinel 的构造

Sentinel 是一个监视器,它可以根据被监视实例的身份和状态来判断应该执行何种动作。

image-20230905110558105

sentinel 的功能

1)监控(Monitoring):

Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。

2)提醒(Notification):

当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。

3)自动故障迁移(Automatic failover):

当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

sentinel 如何发现主库和从库

Sentinel 通过用户给定的配置文件来发现主服务器。

image-20230905110816096

Sentinel 通过向主服务器发送 INFO 命令来自动获得所有从服务器的地址。

image-20230905112141764

Sentinel 会与被监视的主服务器创建两个网络连接:

  • 订阅连接
    • 订阅连接用于订阅指定的频道,从而发现监视同一主服务器的其他 Sentinel。
    • 一个哨兵,可以通过订阅连接,发现其他哨兵
  • 命令连接
    • 通过主库执行 info replication 发现其他从库
    • 切换时,取消从库身份,其他从库指向新主库

发现其他 sentinel

Sentinel 会通过命令连接向被监视的主从服务器发送 “HELLO” 信息,该消息包含 Sentinel 的 IP、端口号、ID 等内容,以此来向其他 Sentinel 宣告自己的存在。与此同时 Sentinel 会通过订阅连接接收其他 Sentinel 的 “HELLO” 信息,以此来发现监视同一个主服务器的其他 Sentinel 。

image-20230905112316131

1)一个 Sentinel 可以与其他多个 Sentinel 进行连接,各个 Sentinel 之间可以互相检查对方的可用性,并进行信息交换。你无须为运行的每个 Sentinel 分别设置其他 Sentinel 的地址,因为 Sentinel 可以通过发布与订阅功能来自动发现正在监视相同主服务器的其他 Sentinel ,这一功能是通过向频道 sentinel:hello 发送信息来实现的。

2)与此类似,你也不必手动列出主服务器属下的所有从服务器,因为 Sentinel 可以通过询问主服务器来获得所有从服务器的信息。每个 Sentinel 会以每两秒一次的频率,通过发布与订阅功能,向被它监视的所有主服务器和从服务器的 sentinel:hello 频道发送一条信息,信息中包含了 Sentinel 的 IP 地址、端口号和运行 ID(runid)。

3)每个 Sentinel 都订阅了被它监视的所有主服务器和从服务器的 sentinel:hello 频道,查找之前未出现过的 sentinel(looking for unknown sentinels)。当一个 Sentinel 发现一个新的 Sentinel 时,它会将新的 Sentinel 添加到一个列表中,这个列表保存了 Sentinel 已知的,监视同一个主服务器的所有其他 Sentinel 。Sentinel 发送的信息中还包括完整的主服务器当前配置(configuration)。如果一个 Sentinel 包含的主服务器配置比另一个 Sentinel 发送的配置要旧,那么这个 Sentinel 会立即升级到新配置上。

4)在将一个新 Sentinel 添加到监视主服务器的列表上面之前,Sentinel 会先检查列表中是否已经包含了和要添加的 Sentinel 拥有相同运行 ID 或者相同地址(包括 IP 地址和端口号)的 Sentinel ,如果是的话,Sentinel 会先移除列表中已有的那些拥有相同运行 ID 或者相同地址的 Sentinel ,然后再添加新 Sentinel 。

多个 sentinel 之间连接

Sentinel 之间只会互相创建命令连接,用于进行通信。因为已经有主从服务器作为发送和接收 HELLO 信息的中介,所以 Sentinel 之间不会创建订阅连接。

image-20230905112846213

检测实例的状态

Sentinel 使用 PING 命令来检测实例的状态:如果实例在指定的时间内没有返回回复,或者返回错误的回复,那么该实例会被 Sentinel 判断为下线。

Redis 的 Sentinel 中关于下线(down)有两个不同的概念:

1)主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。

2)客观下线(Objectively Down,简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的服务器下线判断。(一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)

image-20230905113044223

如果一个服务器没有在 master-down-after-milliseconds 选项所指定的时间内,对向它送 PING 命令的 Sentinel 返回一个有效回复(valid reply),那么 Sentinel 就会将这个服务器标记为主观下线。

服务器对 PING 命令的有效回复可以是以下三种回复的其中一种:

1) 返回 +PONG 。

2) 返回 -LOADING 错误。

3) 返回 -MASTERDOWN 错误。

如果服务器返回除以上三种回复之外的其他回复,又或者在指定时间内没有回复 Ping 命令,那么 Sentinel 认为服务器返回的回复无效(non-valid)。

注意:一个服务器必须在 master-down-after-milliseconds 毫秒内,一直返回无效回复才会被 Sentinel 标记为主观下线。

举个例子,如果 master-down-after-milliseconds 选项的值为 30000 毫秒(30 秒),那么只要服务器能在每 29 秒之内返回至少一次有效回复,这个服务器就仍然会被认为是处于正常状态的。

从主观下线状态切换到客观下线状态并没有使用严格的法定人数算法(strong quorum algorithm),而是使用了流言协议:如果 Sentinel 在给定的时间范围内,从其他 Sentinel 那里接收到了足够数量的主服务器下线报告,那么 Sentinel 就会将主服务器的状态从主观下线改变为客观下线。如果之后其他 Sentinel 不再报告主服务器已下线,那么客观下线状态就会被移除。

客观下线条件只适用于主服务器:对于任何其他类型的 Redis 实例,Sentinel 在将它们判断为下线前不需要进行协商,所以从服务器或者其他 Sentinel 永远不会达到客观下线条件。

只要一个 Sentinel 发现某个主服务器进入了客观下线状态,这个 Sentinel 就可能会被其他 Sentinel 推选出,并对失效的主服务器执行自动故障迁移操作。

故障转移 FAILOVER 步骤

一次故障转移操作由以下步骤组成:

1)发现主服务器已经进入客观下线状态。

2)基于 Raft leader election 协议 ,进行投票选举

3)如果当选失败,那么在设定的故障迁移超时时间的两倍之后,重新尝试当选。如果当选成功,那么执行以下步骤。

4)选出一个从服务器,并将它升级为主服务器。

5)向被选中的从服务器发送 SLAVEOF NO ONE 命令,让它转变为主服务器。

6)通过发布与订阅功能,将更新后的配置传播给所有其他 Sentinel,其他 Sentinel 对它们自己的配置进行更新。

7)向已下线主服务器的从服务器发送 SLAVEOF 命令,让它们去复制新的主服务器。

8)当所有从服务器都已经开始复制新的主服务器时, leader Sentinel 终止这次故障迁移操作。

image-20230905113932958

每当一个 Redis 实例被重新配置(reconfigured)—— 无论是被设置成主服务器、从服务器、又或者被设置成其他主服务器的从服务器 —— Sentinel 都会向被重新配置的实例发送一个 CONFIG REWRITE 命令,从而确保这些配置会持久化在硬盘里。

选举规则

Sentinel 使用以下规则来选择新的主服务器:

1)在失效主服务器属下的从服务器当中,那些被标记为主观下线已断线、或者最后一次回复 PING 命令的时间大于五秒钟的从服务器都会被淘汰。

2)在失效主服务器属下的从服务器当中,那些与失效主服务器连接断开的时长超过 down-after 选项指定的时长十倍的从服务器都会被淘汰。

3)在经历了以上两轮淘汰之后剩下来的从服务器中,我们选出复制偏移量(replication offset)最大的那个从服务器作为新的主服务器。

4)如果复制偏移量不可用,或者从服务器的复制偏移量相同,那么带有最小运行 ID 的那个从服务器成为新的主服务器。

Sentinel 自动故障迁移切换后如何保持一致性特质:

1)Sentinel 自动故障迁移使用 Raft 算法来选举领头(leader)Sentinel ,从而确保在一个给定的周期(epoch)里,只有一个领头产生。

2)这表示在同一个周期中, 不会有两个 Sentinel 同时被选中为领头,并且各个 Sentinel 在同一个节点中只会对一个领头进行投票。

3)更高的配置节点总是优于较低的节点,因此每个 Sentinel 都会主动使用更新的节点来代替自己的配置。 简单来说,我们可以将 Sentinel 配置看作是一个带有版本号的状态。一个状态会以最后写入者胜出(last-write-wins)的方式(也即是,最新的配置总是胜出)传播至所有其他 Sentinel。

sentinel 持久化配置

Sentinel 状态的持久化:

1)Sentinel 的状态会被持久化在 Sentinel 配置文件里面。

2)每当 Sentinel 接收到一个新的配置,或者当领头 Sentinel 为主服务器创建一个新的配置时,这个配置会与配置节点一起被保存到磁盘里面。

3)这意味着停止和重启 Sentinel 进程都是安全的。

sentinel 实战及配置讲解

环境准备

主机 IP 角色 应用
db01 10.0.0.51 主库 redis-server redis-client、redis-sentinel
db02 10.0.0.52 从库 redis-server redis-client
db03 10.0.0.53 从库 redis-server redis-client

主从复制

# 解除从库身份
[root@db01 ~]# redis-cli -a 123
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> SLAVEOF no one

# 连接从库(每连接一个从库就执行下方的开启主从命令)
[root@db02 ~]# redis-cli -a 123 -p 6379
[root@db03 ~]# redis-cli -a 123 -p 6379

# 开启主从
127.0.0.1:6379> SLAVEOF 172.16.1.51 6379

# 查看主从复制信息
172.16.1.51:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.16.1.52,port=6379,state=online,offset=5684,lag=1
slave1:ip=172.16.1.53,port=6379,state=online,offset=5684,lag=1
master_failover_state:no-failover
master_replid:3c1ee0f9adad533c2e739a7635d7167b2838cf2b
master_replid2:c5c7e4eaf55e935bb157f40451483e3796daa821
master_repl_offset:5684
second_repl_offset:5657
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:5657
repl_backlog_histlen:28

sentinel monitor mymaster 127.0.0.1 6379 2

Sentinel 去监视一个名为 mymaster 的主服务器,这个主服务器的 IP 地址为 127.0.0.1,端口号为 6379,而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意(只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行,不过要注意,无论你设置要多少个 Sentinel 同意才能判断一个服务器失效,一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持,才能发起一次自动故障迁移,并预留一个给定的配置节点(configuration Epoch,一个配置节点就是一个新主服务器配置的版本号)。换句话说,在只有少数(minority)Sentinel 进程正常运作的情况下,Sentinel 是不能执行自动故障迁移的。

sentinel down-after-milliseconds mymaster 5000

指定了 Sentinel 认为服务器已经断线所需的毫秒数。如果服务器在给定的毫秒数之内,没有返回 Sentinel 发送的 Ping 命令的回复,或者返回一个错误,那么 Sentinel 将这个服务器标记为主观下线(subjectively down,简称 SDOWN)。不过只有一个 Sentinel 将服务器标记为主观下线并不一定会引起服务器的自动故障迁移:只有在足够数量的 Sentinel 都将一个服务器标记为主观下线之后,服务器才会被标记为客观下线(objectively down, 简称 ODOWN ),这时自动故障迁移才会执行。

sentinel failover-timeout mymaster 180000

自动故障切换的超时时间

sentinel parallel-syncs mymaster 1

在执行故障转移时,最多可以有多少个从服务器同时对新的主服务器进行同步,这个数字越小,完成故障转移所需的时间就越长。如果从服务器被设置为允许使用过期数据集(参见对 redis.conf 文件中对 slave-serve-stale-data 选项的说明),那么你可能不希望所有从服务器都在同一时间向新的主服务器发送同步请求,因为尽管复制过程的绝大部分步骤都不会阻塞从服务器,但从服务器在载入主服务器发来的 RDB 文件时,仍然会造成从服务器在一段时间内不能处理命令请求:如果全部从服务器一起对新的主服务器进行同步,那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。可以通过将这个值设为 1 来保证每次只有一个从服务器处于不能处理命令请求的状态。

配置 sentinel

# 编辑sentinel配置文件
[root@db01 ~]# vim /app/redis/sentinel.conf 
port 26379
dir "/app/redis/data"
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel down-after-milliseconds mymaster 5000
daemonize yes
sentinel auth-pass mymaster 123

mymaster(集群名,如果在项目中需要按照项目名来)

# 启动sentinel(最好使用system管理)
[root@db01 ~]# redis-sentinel /app/redis/sentinel.conf

# 查看哨兵配置文件
[root@db01 redis]# cat /app/redis/sentinel.conf 
port 26379
dir "/app/redis-7.2.0/data"
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel down-after-milliseconds mymaster 5000
daemonize yes
sentinel auth-pass mymaster 123

# Generated by CONFIG REWRITE
protected-mode no
pidfile "/var/run/redis.pid"
latency-tracking-info-percentiles 50 99 99.9
user default on nopass sanitize-payload ~* &* +@all
sentinel myid 21ba9bfff265d448ca6d169411cbfac2e28c305b
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 1
sentinel current-epoch 1

sentinel known-replica mymaster 172.16.1.53 6379

sentinel known-replica mymaster 172.16.1.52 6379

哨兵模式实战

# 关闭redis
[root@db01 ~]# systemctl stop redis

# 查看哨兵模式日志
35433:X 05 Sep 2023 12:09:19.524 # +sdown master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.524 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
35433:X 05 Sep 2023 12:09:19.524 # +new-epoch 1
35433:X 05 Sep 2023 12:09:19.524 # +try-failover master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.525 * Sentinel new configuration saved on disk
35433:X 05 Sep 2023 12:09:19.525 # +vote-for-leader d9d67fa1098559bb1ed653a45dc376d49b2e26b8 1
35433:X 05 Sep 2023 12:09:19.525 # +elected-leader master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.525 # +failover-state-select-slave master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.591 # +selected-slave slave 172.16.1.53:6379 172.16.1.53 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.591 * +failover-state-send-slaveof-noone slave 172.16.1.53:6379 172.16.1.53 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.682 * +failover-state-wait-promotion slave 172.16.1.53:6379 172.16.1.53 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.976 * Sentinel new configuration saved on disk
35433:X 05 Sep 2023 12:09:19.976 # +promoted-slave slave 172.16.1.53:6379 172.16.1.53 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:19.976 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:20.047 * +slave-reconf-sent slave 172.16.1.52:6379 172.16.1.52 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:21.062 * +slave-reconf-inprog slave 172.16.1.52:6379 172.16.1.52 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:21.062 * +slave-reconf-done slave 172.16.1.52:6379 172.16.1.52 6379 @ mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:21.134 # +failover-end master mymaster 127.0.0.1 6379
35433:X 05 Sep 2023 12:09:21.134 # +switch-master mymaster 127.0.0.1 6379 172.16.1.53 6379
35433:X 05 Sep 2023 12:09:21.136 * +slave slave 172.16.1.52:6379 172.16.1.52 6379 @ mymaster 172.16.1.53 6379
35433:X 05 Sep 2023 12:09:21.136 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 172.16.1.53 6379
35433:X 05 Sep 2023 12:09:21.137 * Sentinel new configuration saved on disk
35433:X 05 Sep 2023 12:09:26.147 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 172.16.1.53 6379

# 53被提升为主库
172.16.1.53:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=172.16.1.52,port=6379,state=online,offset=41755,lag=1
master_failover_state:no-failover
master_replid:9e090fe0af829c5ba67cea24897af229cfbde702
master_replid2:774326819d3af28b0c639c4e69f1ff6806454966
master_repl_offset:41892
second_repl_offset:35168
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:32700
repl_backlog_histlen:9193
# 52指向53
172.16.1.52:6379>  info replication 
# Replication
role:slave
master_host:172.16.1.53
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:41056
slave_repl_offset:41056
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:9e090fe0af829c5ba67cea24897af229cfbde702
master_replid2:774326819d3af28b0c639c4e69f1ff6806454966
master_repl_offset:41056
second_repl_offset:35168
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:32700
repl_backlog_histlen:8357

# 重启51的redis
[root@db01 ~]# systemctl start redis

# 查看哨兵模式日志
35433:X 05 Sep 2023 12:15:42.116 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 172.16.1.53 6379
35433:X 05 Sep 2023 12:15:52.118 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 172.16.1.53 6379
35433:X 05 Sep 2023 12:15:53.220 * +slave slave 172.16.1.51:6379 172.16.1.51 6379 @ mymaster 172.16.1.53 6379
35433:X 05 Sep 2023 12:15:53.221 * Sentinel new configuration saved on disk

# 51变为从库
[root@db01 ~]# redis-cli -h 172.16.1.51
172.16.1.51:6379> info replication
# Replication
role:slave
master_host:172.16.1.53
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:48197
slave_repl_offset:48197
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:9e090fe0af829c5ba67cea24897af229cfbde702
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:48197
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:48198
repl_backlog_histlen:0

sentinel 管理命令(不常用)

#连接sentinel管理端口
[root@db01 26380]# redis-cli -p 26380

#检测状态,返回PONG
127.0.0.1:26380> PING
PONG

#列出所有被监视的主服务器
127.0.0.1:26380> SENTINEL masters

#列出所有被监视的从服务器
127.0.0.1:26380> SENTINEL slaves mymaster

#返回给定名字的主服务器的IP地址和端口号
127.0.0.1:26380> SENTINEL get-master-addr-by-name mymaster
1) "127.0.0.1"
2) "6380"

#重置所有名字和给定模式
127.0.0.1:26380> SENTINEL reset mymaster

#当主服务器失效时,在不询问其他Sentinel意见的情况下,强制开始一次自动故障迁移。
127.0.0.1:26380> SENTINEL failover mymaster