NoSQL 数据库系列(九):Redis sentinel 哨兵集群原理、部署及数据恢复

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

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

哨兵机制(Redis Sentinel)

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

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

attachments-2023-06-ZYikONLG647ed8a1d25e2,png

哨兵实现了什么功能呢?

下面是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 建立网络连接。

attachments-2023-06-hIME4qFj647ed8bd83fc9,jpg

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

哨兵监控 Redis 库

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

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

attachments-2023-06-Mq7ZKsO9647ed8dc13eda,jpg

主库下线的判定

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

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

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

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

attachments-2023-06-RXkWI9iO647ed9083ede4,jpg

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

哨兵集群的选举

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

  • 为什么必然会出现选举/共识机制

为了避免哨兵的单点情况发生,所以需要一个哨兵的分布式集群。作为分布式集群,必然涉及共识问题(即选举问题);同时故障的转移和通知都只需要一个主的哨兵节点就可以了。

  • 哨兵的选举机制是什么样的

哨兵的选举机制其实很简单,就是一个Raft选举算法:选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举

  • 任何一个想成为 Leader 的哨兵,要满足两个条件
    • 第一,拿到半数以上的赞成票;
    • 第二,拿到的票数同时还需要大于等于哨兵配置文件中的 quorum 值。

以 3 个哨兵为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。

更进一步理解

这里很多人会搞混 判定客观下线 和 是否能够主从切换(用到选举机制) 两个概念,我们再看一个例子。

Redis 1主4从,5个哨兵,哨兵配置quorum为2,如果3个哨兵故障,当主库宕机时,哨兵能否判断主库“客观下线”?能否自动切换?

经过实际测试:

1、哨兵集群可以判定主库“主观下线”。由于quorum=2,所以当一个哨兵判断主库“主观下线”后,询问另外一个哨兵后也会得到同样的结果,2个哨兵都判定“主观下线”,达到了quorum的值,因此,哨兵集群可以判定主库为“客观下线”

2、但哨兵不能完成主从切换。哨兵标记主库“客观下线后”,在选举“哨兵领导者”时,一个哨兵必须拿到超过多数的选票(5/2+1=3票)。但目前只有2个哨兵活着,无论怎么投票,一个哨兵最多只能拿到2票,永远无法达到N/2+1选票的结果。

新主库的选出

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

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

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

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

    attachments-2023-06-ARr8ic0v647ed93b00c0b,png

    故障转移流程如下

    attachments-2023-06-MLzJD3zu647ed94ad0ac2,png

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

    转移之后

    attachments-2023-06-6V2xjgwp647ed95d50f58,png

    搭建redis哨兵集群

    环境准备

    attachments-2023-06-0qEEWxHY647ed97c40adf,png

    配置哨兵集群步骤:

    • 1.在所有节点搭建redis
    • 2.配置主从复制,一主两从
    • 3.在所有节点配置sentinel,启动sentinel后,配置文件会自动增加

    在所有机器上部署redis

    192.168.81.210配置

    1.创建redis部署路径
    [root@redis-1 ~]# mkdir -p /data/redis_cluster/redis_6379/{conf,pid,logs,data}
        
    2.下载redis    
    [root@redis-1 ~]# mkdir /data/soft
    [root@redis-1 ~]# cd /data/soft
    [root@redis-1 /data/soft]# wget https://repo.huaweicloud.com/redis/redis-3.2.9.tar.gz

    3.便于安装redis
    [root@redis-1 /data/soft]# tar xf redis-3.2.9.tar.gz -C /data/redis_cluster/
    [root@redis-1 /data/soft]# cd /data/redis_cluster/
    [root@redis-1 /data/redis_cluster]# ln -s redis-3.2.9/ redis
    [root@redis-1 /data/redis_cluster]# cd redis/src
    [root@redis-1 /data/redis_cluster/redis]# make && make install

    4.准备配置文件
    [root@redis-1 ~]# vim /data/redis_cluster/redis_6379/conf/redis_6379.conf 
    daemonize yes
    bind 192.168.81.210 127.0.0.1
    port 6379
    pidfile /data/redis_cluster/redis_6379/pid/redis_6379.pid
    logfile /data/redis_cluster/redis_6379/logs/redis_6379.log
    databases 16
    dbfilename redis_6379.rdb
    dir /data/redis_cluster/redis_6379/data/
    save 900 1
    save 300 100
    save 60 10000

    5.启动redis
    [root@redis-1 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf 

    192.168.81.220配置

    由于redis-1已经部署好了一套redis,我们可以直接复制过来使用

    1.使用rsync将redis-1的redis目录拷贝过来你
    [root@redis-1 ~]# rsync -avz /data root@192.168.81.220:/

    2.查看拷贝过来的目录文件
    [root@redis-2 ~]# ls  /data/redis_cluster/
    redis  redis-3.2.9  redis_6379
    [root@redis-2 ~]# ls  /data/redis_cluster/redis_6379/
    conf  data  logs  pid

    3.编译安装redis,使系统能使用redis命令
    直接执行make install即可,因为编译步骤在redis-1已经做了
    [root@redis-2 ~]# cd /data/redis_cluster/redis-3.2.9/
    [root@redis-2 /data/redis_cluster/redis-3.2.9]# make install

    4.修改redis配置文件
    [root@redis-2 ~]# vim /data/redis_cluster/redis_6379/conf/redis_6379.conf 
    bind 192.168.81.220 127.0.0.1

    5.启动redis
    [root@redis-2 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf

    192.168.81.230配置

    由于redis-1已经部署好了一套redis,我们可以直接复制过来使用

    1.使用rsync将redis-1的redis目录拷贝过来你
    [root@redis-1 ~]# rsync -avz /data root@192.168.81.230:/

    2.查看拷贝过来的目录文件
    [root@redis-3 ~]# ls  /data/redis_cluster/
    redis  redis-3.2.9  redis_6379
    [root@redis-3 ~]# ls  /data/redis_cluster/redis_6379/
    conf  data  logs  pid

    3.编译安装redis,使系统能使用redis命令
    直接执行make install即可,因为编译步骤在redis-1已经做了
    [root@redis-3 ~]# cd /data/redis_cluster/redis-3.2.9/
    [root@redis-3 /data/redis_cluster/redis-3.2.9]# make install

    4.修改redis配置文件
    [root@redis-3 ~]# vim /data/redis_cluster/redis_6379/conf/redis_6379.conf 
    bind 192.168.81.230 127.0.0.1

    5.启动redis
    [root@redis-3 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf

    三台redis部署完成

    [root@redis-1 ~]# ps aux | grep redis
    root      21860  0.1  0.5 139020  9740 ?        Ssl  09:36   0:16 redis-server 192.168.81.210:6379
    root      25296  0.0  0.0 112724   984 pts/0    S+   13:15   0:00 grep --color=auto redis

    [root@redis-1 ~]# ssh 192.168.81.220 "ps aux | grep redis"
    root      47658  0.1  0.5 141068 10780 ?        Ssl  1月28   1:24 redis-server 192.168.81.220:6379
    root      63254  0.0  0.0 113176  1588 ?        Ss   13:15   0:00 bash -c ps aux | grep redis
    root      63271  0.0  0.0 112724   968 ?        S    13:15   0:00 grep redis

    [root@redis-1 ~]# ssh 192.168.81.230 "ps aux | grep redis"
    root      56584  0.1  0.7 136972  7548 ?        Ssl  13:13   0:00 redis-server 192.168.81.230:6379
    root      56644  0.0  0.1 113176  1588 ?        Ss   13:15   0:00 bash -c ps aux | grep redis
    root      56661  0.0  0.0 112724   968 ?        S    13:15   0:00 grep redis

    配置redis主从

    要在两台slave上同步主库配置

    1.配置主从复制
    [root@redis-2 ~]# redis-cli 
    127.0.0.1:6379> SLAVEOF 192.168.81.210 6379
    OK  

    [root@redis-3 ~]# redis-cli 
    127.0.0.1:6379> SLAVEOF 192.168.81.220 6379
    OK


    2.主库新建一个key
    127.0.0.1:6379> set name jiangxl
    OK


    3.从库查看是否复制
    [root@redis-2 ~]# redis-cli 
    127.0.0.1:6379> get name
    "jiangxl"
    [root@redis-3 ~]# redis-cli 
    127.0.0.1:6379> get name
    "jiangxl"

    部署哨兵进程sentinel

    配置文件解释

    sentinel monitor mymaster 192.168.81.210 6379 2   //设置主节点信息,mymaster是主节点别名,就是随便起一个名字,然后填写主节点的ip地址,2表示当主节点挂掉后,有2个sentinel同意后才会选举新的master,一组哨兵集群,要把名称都写成一样的

    sentinel down-after-milliseconds mymaster 3000   //主库宕机多少秒,从库在进行切换,因为有时因为网络波动,如果只要主库一宕机就切换主从,那么redis可能一直处于正在切换状态
    sentinel parallel-syncs mymaster 1     //允许几个节点同时向主库同步数据
    sentinel failover-timeout mymaster 18000   //故障转移超时时间,当从库同步主库的rdb文件,多长时间没有同步完就认为超时

    三台redis服务器都要按如下配置,已经将配置文件中的bind写成了系统变量,在配合cat写入到文件,因此直接执行如下命令即可

    1.创建哨兵服务配置路径
    mkdir -p /data/redis_cluster/redis_26379/{conf,data,pid,logs} 

    2.写入哨兵配置文件
    cat > /data/redis_cluster/redis_26379/conf/redis_26379.conf <<EOF
    bind $(ifconfig | awk 'NR==2{print $2}')
    port 26379
    daemonize yes
    logfile /data/redis_cluster/redis_26379/logs/redis_26379.log
    dir /data/redis_cluster/redis_26379/data
    sentinel monitor mymaster 192.168.81.210 6379 2
    sentinel down-after-milliseconds mymaster 3000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 18000
    EOF

    配置完记得查看下配置文件bind一列是否是各自主机的ip地址

  • attachments-2023-06-S6j73g8q647ed9faee40c,png

    启动哨兵观察配置文件的变化

    三台机器都这么操作启动哨兵

     redis-sentinel /data/redis_cluster/redis_26379/conf/redis_26379.conf

    观察哨兵启动前后配置文件的变化

    启动前

    attachments-2023-06-bxybfU8v647eda1a134f6,png

    启动后

    attachments-2023-06-jWx1y6VP647eda33b3aa4,png

    每台哨兵主机都自动增加了一个myid的配置,这个就是当主库挂掉后,哨兵选举的依据,判断谁的myid大谁就当选为主库

    每台哨兵主机还自动增加了sentinel known-sentinel这个配置,这个配置每个哨兵会记录集群中其他节点的id号,这样就能够实现信息共享,即使应用在询问哨兵进程谁是主库,这时由于每个哨兵进程都有其他节点的信息,因此就能里面告诉应用谁是主库。

    attachments-2023-06-XsZRLXDt647eda4cb0cb3,png

    模拟主库故障验证应用是否可用

    配置完哨兵后,每个节点上都有集群的信息共享,当主库挂掉后,哨兵进程确认主库下线了,哨兵根据各自的id大小选举新的主库,接替主库的工作,保证应用程序不受影响,当主库修复好后,在通过提权的方式先同步目前主库的数据,在让自身成为主库。

    #关闭主库的redis服务,reids正常关闭,sentinel直接kill
    [root@redis-1 ~]# redis-cli shutdown
    [root@redis-1 ~]# pkill redis

    #查看配置文件看看谁的myid大
    redis-2的myid比较大
    [root@redis-1 ~]# grep 'known-sentinel' /data/redis_cluster/redis_26379/conf/redis_26379.conf 
    sentinel known-sentinel mymaster 192.168.81.220 26379 df44bb3e9fdf8c635628b1ae724b2db7d3ef144c
    sentinel known-sentinel mymaster 192.168.81.230 26379 de282d14bb0a79df90603eb92243cd1f362dd46d

    #测试redis-2是否可用写入数据
    可以写入数据,redis-2被选为主库
    [root@redis-1 ~]# redis-cli -h 192.168.81.220 set gzzy_test guzhangzhuanyi
    OK
    [root@redis-1 ~]#  redis-cli -h 192.168.81.220 config get slaveof
    1) "slaveof"
    2) ""

    #测试redis-3是否可用写入数据
    写入数据失败,并且同步的是redis-2的数据,因此redis-2为主库
    [root@redis-1 ~]# redis-cli -h 192.168.81.230 set kkkk111 vvv
    (error) READONLY You can't write against a read only slave.
    [root@redis-1 ~]# redis-cli -h 192.168.81.230 config get slaveof
    1) "slaveof"
    2) "192.168.81.220 6379"

    attachments-2023-06-eOSTq4XH647eda7fb4177,png

    主库挂掉其他节点配置文件的变化

    主库挂掉后,其他两个节点选举出master后,配置文件也会填写为新master的地址。

    attachments-2023-06-cZEGP3Lg647edaf684c28,png

    至此,一个 Redis 哨兵集群架构说部署完成了。

    Redis 哨兵集群主库故障数据恢复实践

    当主库修复后重新上线首先通过哨兵知道谁是当前的主库,然后就会去找主库同步数据,并且会自动修改配置文件,当数据同步后,想恢复的主库重新成为主库则需要把主库的权重调高,然后重新选举,这时原来的主库就能成为新的主库,调整完再将主库的权重值调成默认的。

    实现思路

    • 1.将故障的主库重新恢复
    • 2.查看当前的主从状态,验证由于主库宕机,与从库产生的数据是否同步
    • 3.调整权重值
    • 4.重新选举,使原来的主库变成新的主库
    • 5.恢复的主库重新成为新的主库后,要把调整的权重值全部变成默认值

    主库可以重新加入哨兵集群的前提:剩余的两个节点必须有一个是master,且这两个节点配置文件已经指定了新的master地址

    恢复损坏的主库

    #恢复主库
    [root@redis-1 ~]# redis-server /data/redis_cluster/redis_6379/conf/redis_6379.conf 
    [root@redis-1 ~]
    [root@redis-1 ~]# redis-sentinel /data/redis_cluster/redis_26379/conf/redis_26379.conf

    #查看其他两个节点的日志输出,任意一个节点都会输出,表示redis-1已经加入集群了
    tail -f /data/redis_cluster/redis_26379/logs/redis_26379.log 
    78223:X 30 Jan 12:05:09.073 # -sdown sentinel ac621a57296db0cead07751a4f0a19c570daa7f9 192.168.81.210 26379 @ mymaster 192.168.81.220 6379,

    attachments-2023-06-Guaq5CoC647edb8a5bc6c,png

    查看恢复的主库redis-1配置文件

    [root@redis-1 ~]# cat /data/redis_cluster/redis_26379/conf/redis_26379.conf
    bind 192.168.81.210
    port 26379
    daemonize yes
    logfile "/data/redis_cluster/redis_26379/logs/redis_26379.log"
    dir "/data/redis_cluster/redis_26379/data"
    sentinel myid ac621a57296db0cead07751a4f0a19c570daa7f9
    sentinel monitor mymaster 192.168.81.220 6379 2

    可以看到已经自动修改为当前库的地址

    attachments-2023-06-DhHLofZS647edbc53df62,png

    查看恢复的主库redis-1的主从关系

    #已经同步了当前主库redis-2
    [root@redis-1 ~]# redis-cli 
    127.0.0.1:6379> CONFIG GET slaveof
    1) "slaveof"
    2) "192.168.81.220 6379"

    #已经可以看到主库宕机阶段,从库变为主库产生的最新数据
    127.0.0.1:6379> get gzzy_test
    "guzhangzhuanyi"

    配置恢复的主库的权重值,使其重新选举为主库

    哨兵的选举首先是

    • 查看谁的权重优先级比较高的当选为主库
    • 权重优先级一致,就比较id,id大的当选
    #查看其他两个节点的权重值
    [root@redis-1 ~]# redis-cli -h 192.168.81.220 -p 6379 config get slave-priority 
    1) "slave-priority"
    2) "100"
     
    [root@redis-1 ~]# redis-cli -h 192.168.81.230 -p 6379 config get slave-priority 
    1) "slave-priority"
    2) "100"

    #将其他两个节点的权重值改为0
    [root@redis-1 ~]# redis-cli -h 192.168.81.220 -p 6379 config set slave-priority 0
    OK
    [root@redis-1 ~]# redis-cli -h 192.168.81.230 -p 6379 config set slave-priority 0
    OK

    #设置恢复的主库的权限优先级高于其他两个节点
    [root@redis-1 ~]# redis-cli -h 192.168.81.210 -p 6379 config set slave-priority 150
    OK
    [root@redis-1 ~]# redis-cli -h 192.168.81.210 -p 6379 config get slave-priority 
    1) "slave-priority"
    2) "150"

    #重新选举
    [root@redis-1 ~]# redis-cli -h 192.168.81.210 -p 26379 sentinel failover mymaster

    #查看其他节点sentinel输出的日志
    [root@redis-3 ~]# tail -f /data/redis_cluster/redis_26379/logs/redis_26379.log
    78223:X 30 Jan 12:32:27.591 * +convert-to-slave slave 192.168.81.220:6379 192.168.81.220 6379 @ mymaster 192.168.81.210 6379

    根据日志的输出,可以明显的看出调整了redis-1的权重优先级为150,比其他两个节点的高,因此redis-1就变成了主库。

    attachments-2023-06-HbnXnKyw647edbe2ef9fc,png

    查看节点的主从复制关系。

    主库没有同步的库,其他两个节点都同步redis-1的主库。

    [root@redis-1 ~]# redis-cli -h 192.168.81.210 -p 6379 config get slaveof
    1) "slaveof"
    2) ""

    [root@redis-1 ~]# redis-cli -h 192.168.81.220 -p 6379 config get slaveof
    1) "slaveof"
    2) "192.168.81.210 6379"

    [root@redis-1 ~]# redis-cli -h 192.168.81.230 -p 6379 config get slaveof
    1) "slaveof"
    2) "192.168.81.210 6379"

    将权重值调整为默认值

    将权重值调整为默认值,方便下次选举时作为判断条件。

    [root@redis-1 ~]# redis-cli -h 192.168.81.210 -p 6379 config set slave-priority 100
    OK
    [root@redis-1 ~]# redis-cli -h 192.168.81.220 -p 6379 config set slave-priority 100
    OK
    [root@redis-1 ~]# redis-cli -h 192.168.81.230 -p 6379 config set slave-priority 100
    OK

  • 发表于 2023-06-06 15:00
  • 阅读 ( 36 )

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
石天
石天

437 篇文章

作家榜 »

  1. shitian 662 文章
  2. 石天 437 文章
  3. 每天惠23 33 文章
  4. 小A 29 文章