文章目录
- 哨兵机制
- 哨兵集群的组建
- 哨兵监控Redis
- 主库下线的判定
- 哨兵集群的选举
- 新主库的选出
- 故障的转移
- redis和sentinel 搭建一主多从高可用集群
- redis主从(一主二从)配置
- sentinel(哨兵)集群
- 高可用场景演示
- 场景一:主机master宕机
- 场景二:宕机的主机恢复
- 场景三:从机的宕机和恢复
哨兵机制
在上文主从复制的基础上,如果主节点出现故障该怎么办呢? 在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制,它有效地解决了主从复制模式下故障转移的问题。
Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入。哨兵的核心功能是主节点的自动故障转移。
下图是一个典型的哨兵集群监控的逻辑图:
哨兵实现了什么功能呢?
下面是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 建立网络连接。
通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。
哨兵监控Redis
哨兵监控什么呢?怎么监控呢?
这是由哨兵向主库发送 INFO 命令来完成的。就像下图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。
主库下线的判定
哨兵如何判断主库已经下线了呢?
首先要理解两个概念:主观下线和客观下线
- 主观下线:任何一个哨兵都是可以监控探测,并作出Redis节点下线的判断;
- 客观下线:由哨兵集群共同决定Redis节点是否下线;
当某个哨兵(如下图中的哨兵2)判断主库“主观下线”后,就会给其他哨兵发送 is-master-down-by-addr 命令。接着,其他哨兵会根据自己和主库的连接情况,做出 Y 或 N 的响应,Y 相当于赞成票,N 相当于反对票。
如果赞成票数(这里是2)是大于等于哨兵配置文件中的 quorum 配置项
(比如这里如果是quorum=2), 则可以判定主库客观下线了。
哨兵集群的选举
判断完主库下线后,由哪个哨兵节点来执行主从切换呢?这里就需要哨兵集群的选举机制了。
- 为什么必然会出现选举/共识机制?
为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。作为分布式集群,必然涉及共识问题(即选举问题);同时故障的转移和通知都只需要一个主的哨兵节点就可以了。 - 哨兵的选举机制是什么样的?
哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举 - 任何一个想成为 Leader 的哨兵,要满足两个条件:
第一,拿到半数以上的赞成票;
第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。
新主库的选出
主库既然判定客观下线了,那么如何从剩余的从库中选择一个新的主库呢?
- 过滤掉不健康的(下线或断线),没有回复过哨兵ping响应的从节点
- 选择salve-priority从节点优先级最高的
- 选择复制偏移量最大,只复制最完整的从节点
故障的转移
新的主库选择出来后,就可以开始进行故障的转移了。
假设根据我们一开始的图:(我们假设:判断主库客观下线了,同时选出sentinel 3是哨兵leader)
故障转移流程如下:
- 将slave-1脱离原从节点(PS: 5.0 中应该是replicaof no one),升级主节点,
- 将从节点slave-2指向新的主节点
- 通知客户端主节点已更换
- 将原主节点(oldMaster)变成从节点,指向新的主节点
转移之后:
redis和sentinel 搭建一主多从高可用集群redis主从(一主二从)配置
由于redis默认是主库,所以只需要配置从库即可:
- 从默认的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
- 修改两个从机的配置文件的replication部分
replicaof 127.0.0.1 6379
- 分别从三个配置文件启动三个redis-server服务
- 查看主从复制相关信息
(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
注意:
- 如果此时主机宕机了,则整个集群只能执行读,不能执行写。如果主机恢复了(如果主机一直不恢复,可以手动指定一个从机为主机
slaveof no one
,并将其他的从机连接到这个新主机),从机依然可以直接获取到主机的写信息。 - 如果从机宕机了,并且是使用命令行来配置的从机,从机恢复后会变成一个主机(如果是写在配置文件中,则依然是从机),丢失集群信息,但是在从机宕机之前被写入从机的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:
(3) 启动哨兵集群:
高可用场景演示
场景一:主机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