文章目录

  • 哨兵机制
  • 哨兵集群的组建
  • 哨兵监控Redis
  • 主库下线的判定
  • 哨兵集群的选举
  • 新主库的选出
  • 故障的转移
  • redis和sentinel 搭建一主多从高可用集群
  • redis主从(一主二从)配置
  • sentinel(哨兵)集群
  • 高可用场景演示
  • 场景一:主机master宕机
  • 场景二:宕机的主机恢复
  • 场景三:从机的宕机和恢复


哨兵机制

在上文主从复制的基础上,如果主节点出现故障该怎么办呢? 在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的问题。

Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入。哨兵的核心功能是主节点的自动故障转移。

下图是一个典型的哨兵集群监控的逻辑图:

redis solt关闭solt 槽分配 redis关闭哨兵_缓存

哨兵实现了什么功能呢?
下面是Redis官方文档的描述:

  • 监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
  • 自动故障转移(Automatic failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
  • 配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
  • 通知(Notification):哨兵可以将故障转移的结果发送给客户端。

其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。

哨兵集群的组建

上图中哨兵集群是如何组建的呢?哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。

在主从集群中,主库上有一个名为__sentinel__:hello的频道,不同哨兵就是通过它来相互发现,实现互相通信的。在下图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到__sentinel__:hello频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。

redis solt关闭solt 槽分配 redis关闭哨兵_redis_02


通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。

哨兵监控Redis

哨兵监控什么呢?怎么监控呢?

这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。

redis solt关闭solt 槽分配 redis关闭哨兵_配置文件_03

主库下线的判定

哨兵如何判断主库已经下线了呢?

首先要理解两个概念:主观下线客观下线

  • 主观下线:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断;
  • 客观下线:由哨兵集群共同决定Redis节点是否下线;

当某个哨兵(如下图中的哨兵2)判断主库“主观下线”后,就会给其他哨兵发送 is-master-down-by-addr 命令。接着,其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。

redis solt关闭solt 槽分配 redis关闭哨兵_配置文件_04


如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum 配置项(比如这里如果是quorum=2), 则可以判定主库客观下线了。

哨兵集群的选举

判断完主库下线后,由哪个哨兵节点来执行主从切换呢?这里就需要哨兵集群的选举机制了。

  • 为什么必然会出现选举/共识机制?
    为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。作为分布式集群,必然涉及共识问题(即选举问题);同时故障的转移和通知都只需要一个主的哨兵节点就可以了。
  • 哨兵的选举机制是什么样的?
    哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举
  • 任何一个想成为 Leader 的哨兵,要满足两个条件:
    第一,拿到半数以上的赞成票;
    第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。

新主库的选出

主库既然判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?

  • 过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
  • 选择salve-priority从节点优先级最高的
  • 选择复制偏移量最大,只复制最完整的从节点

故障的转移

新的主库选择出来后,就可以开始进行故障的转移了。

假设根据我们一开始的图:(我们假设:判断主库客观下线了,同时选出sentinel 3是哨兵leader)

redis solt关闭solt 槽分配 redis关闭哨兵_数据库_05


故障转移流程如下:

redis solt关闭solt 槽分配 redis关闭哨兵_Redis_06

  • 将slave-1脱离原从节点(PS: 5.0 中应该是replicaof no one),升级主节点,
  • 将从节点slave-2指向新的主节点
  • 通知客户端主节点已更换
  • 将原主节点(oldMaster)变成从节点,指向新的主节点

转移之后:

redis solt关闭solt 槽分配 redis关闭哨兵_Redis_07

redis和sentinel 搭建一主多从高可用集群

redis主从(一主二从)配置

由于redis默认是主库,所以只需要配置从库即可:

  1. 从默认的redis.conf文件复制三个配置文件,并修改port,daemonize,pidfile,logfile,dbfilename,
[root@VM-4-17-centos conf]# ls
redis-mas-6379.conf  redis-sla-6380.conf  redis-sla-6381.conf
  1. 修改两个从机的配置文件的replication部分
replicaof 127.0.0.1 6379
  1. 分别从三个配置文件启动三个redis-server服务
  2. redis solt关闭solt 槽分配 redis关闭哨兵_数据库_08


  3. 查看主从复制相关信息
    (1) 主机中查看:
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=644,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=644,lag=1
master_replid:2a9eb63592f7cb2161be679223b89d966cb12cae
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:644
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:644

(2) 从机中查看:

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:14
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2a9eb63592f7cb2161be679223b89d966cb12cae
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:308
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2a9eb63592f7cb2161be679223b89d966cb12cae
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:281
repl_backlog_histlen:28

注意:

  1. 如果此时主机宕机了,则整个集群只能执行读,不能执行写。如果主机恢复了(如果主机一直不恢复,可以手动指定一个从机为主机 slaveof no one,并将其他的从机连接到这个新主机),从机依然可以直接获取到主机的写信息。
  2. 如果从机宕机了,并且是使用命令行来配置的从机,从机恢复后会变成一个主机(如果是写在配置文件中,则依然是从机),丢失集群信息,但是在从机宕机之前被写入从机的rdb文件中的数据依然能够被读取到;如果从机再次连接到主机,会同步主机中的数据。

在实际开发中会使用配置文件进行配置:

sentinel(哨兵)集群

参考文章:https://cloud.tencent.com/developer/article/1894341

(1) 哨兵的配置文件sentinel.cong,配置参数详解如下:

# 是否开启保护模式,默认开启。
protected-mode:no
# 哨兵sentinel实例运行的端口,默认26379  
port 26379
# 是否设置为后台启动。默认为no
daemonize yes
# pidfile
pidfile /var/run/redis-sentinel.pid
# 哨兵sentinel的日志文件,默认无
logfile ""
# 哨兵sentinel的工作目录,默认为 /tmp
dir /tmp

# 哨兵sentinel监控的redis主节点的 
## ip:主机ip地址
## port:哨兵端口号
## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。)
## quorum:当quorum个sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了  
# sentinel monitor <master-name> <ip> <redis-port> <quorum> 
## 这里IP不要用127.0.0.1
sentinel monitor mymaster 127.0.0.1 6379 2

# 当在Redis实例中开启了requirepass,所有连接Redis实例的客户端都要提供密码。这里提示下,Redis,master实例也需要配置masteruth,否则master实例下线重新上线后,会无法加入到集群中
# sentinel auth-pass <master-name> <password>  
sentinel auth-pass mymaster 123456  

# 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒  
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000  

# 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1  

# 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面:
## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。  
## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。  
## 3. 当想要取消一个正在进行的failover时所需要的时间。
## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了
# sentinel failover-timeout <master-name> <milliseconds>  
sentinel failover-timeout mymaster 180000

# 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
# 对于脚本的运行结果有以下规则:  
## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。
## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。  
## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# sentinel notification-script <master-name> <script-path>  
sentinel notification-script mymaster /var/redis/notify.sh

# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh


#################security#######################
sentinel deny-scripts-reconfig yes

# SENTINEL rename-command mymaster CONFIG GUESSME

(2) 哨兵集群的配置:
这里仅有一个sentinel.conf,其他的sentinel.conf 修改下port,pidfile和logfile 即可:

# bind 127.0.0.1 192.168.1.1
#
# protected-mode no

port 26379

daemonize yes

pidfile "/var/run/redis-sentinel-1.pid"

logfile "/root/redis/sentinel-cluster/redis-sentinel-1.log"

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

dir "/root/redis/sentinel-cluster"

sentinel myid 80d11a1ddd8b7dccc60734e8ebcab9b5a0fe0bbf

# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd

# sentinel auth-user <master-name> <username>

sentinel deny-scripts-reconfig yes

# requirepass <password>

sentinel monitor mymaster 127.0.0.1 6379 2

sentinel config-epoch mymaster 0

# SCRIPTS EXECUTION
# sentinel notification-script mymaster /var/redis/notify.sh

# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

# SECURITY
sentinel leader-epoch mymaster 0

# SENTINEL rename-command mymaster CONFIG CONFIG

我们一共配置了三个sentinel.conf:

redis solt关闭solt 槽分配 redis关闭哨兵_配置文件_09

(3) 启动哨兵集群:

redis solt关闭solt 槽分配 redis关闭哨兵_数据库_10

高可用场景演示

场景一:主机master宕机

(1) 关闭master

127.0.0.1:6379> shutdown
not connected> exit

(2) 查看选举新的master的过程和显示了failover的过程,整个日志信息还是比较完整的。最后选举了6381为新的master

13195:X 22 May 2022 11:36:44.394 # +sdown master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.460 # +odown master mymaster 127.0.0.1 6379 #quorum 2/2
13195:X 22 May 2022 11:36:44.460 # +new-epoch 3
13195:X 22 May 2022 11:36:44.460 # +try-failover master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.465 # +vote-for-leader 60c6d55d022434a6d4c186d245b94201c8f7ce10 3
13195:X 22 May 2022 11:36:44.474 # cf3149501099cf183c9bc45ac6696b7604d1d21d voted for 60c6d55d022434a6d4c186d245b94201c8f7ce10 3
13195:X 22 May 2022 11:36:44.474 # 54cb27c54bed670d11c1ef2b0c795516e00fae9d voted for 60c6d55d022434a6d4c186d245b94201c8f7ce10 3
13195:X 22 May 2022 11:36:44.531 # +elected-leader master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.531 # +failover-state-select-slave master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.593 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.593 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:44.665 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:45.410 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:45.410 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:45.471 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:45.630 # -odown master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:46.414 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:46.415 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:46.504 # +failover-end master mymaster 127.0.0.1 6379
13195:X 22 May 2022 11:36:46.504 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381
13195:X 22 May 2022 11:36:46.505 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381
13195:X 22 May 2022 11:36:46.505 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
13195:X 22 May 2022 11:37:16.552 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

(3) 查看6381对应replication信息

127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=84382,lag=0
master_replid:ae94fe94fafc7dec31b7ded7f045a338b165b4f2
master_replid2:3bcdfbb64ecd652ccfda6a45485c2278cf064cd6
master_repl_offset:84515
second_repl_offset:12022
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:84515

场景二:宕机的主机恢复

(1) 启动6379,发现它成为了6381的从机

[root@VM-4-17-centos redis]# redis-server ./conf/redis-mas-6379.conf 
[root@VM-4-17-centos redis]# redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:98069
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:ae94fe94fafc7dec31b7ded7f045a338b165b4f2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98069
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:92818
repl_backlog_histlen:5252

场景三:从机的宕机和恢复

(1) 关闭6380

127.0.0.1:6380> SHUTDOWN
not connected>

(2) sentinel日志

13195:X 22 May 2022 11:37:16.552 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381

(3) 查看6381的replication信息

127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:182447
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:ae94fe94fafc7dec31b7ded7f045a338b165b4f2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:182447
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:166376
repl_backlog_histlen:16072

(4) 重启6380

[root@VM-4-17-centos redis]# redis-server ./conf/redis-sla-6380.conf

(5) 查看6381的replication信息

127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6379,state=online,offset=1121574,lag=0
slave1:ip=127.0.0.1,port=6380,state=online,offset=1121441,lag=1
master_replid:ae94fe94fafc7dec31b7ded7f045a338b165b4f2
master_replid2:3bcdfbb64ecd652ccfda6a45485c2278cf064cd6
master_repl_offset:1121574
second_repl_offset:12022
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:72999
repl_backlog_histlen:1048576