redis哨兵故障转移及实现

在上篇文章中docker-compose搭建redis-sentinel成功的搭建了1主2从3哨兵。

Sentinel命令

sentinel是一个特殊的redis节点,它有自己专属的api

  • sentinel masters 显示被监控的所有master以及它们的状态。
  • sentinel master <master name> 显示指定master的信息和状态。
  • sentinel slaves <master name> 显示指定master的所有slave及它们的状态。
  • sentinel sentinels <master name> 显示指定mastersentinel节点集合(不包含当前节点)。
  • sentinel get-master-addr-by-name <master name> 返回指定masteripport,如果正在进行failover或者failover已经完成,将会显示被提升为masterslaveipport
  • sentinel failover <master name>强制sentinel执行failover,并且不需要得到其它sentinel的同意。但是failover后会将最新的配置发送给其它sentinel

sentinel masters

展示所有被监控的主节点状态及相关信息:

127.0.0.1:26380> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "192.168.3.2"
    5) "port"
    6) "6379"
…………………………………………………………

sentinel master <master name>

展示指定<master name>状态以及相关的信息:

127.0.0.1:26380> sentinel master mymaster
 1) "name"
 2) "mymaster"
 3) "ip"
 4) "192.168.3.2"
 5) "port"
 6) "6379"
 ………………………………

sentinel slaves <master name>

展示指定 <master name>的从节点状态以及相关的统计信息:

127.0.0.1:26380> sentinel slaves mymaster
1)  1) "name"
    2) "192.168.3.4:6379"
    3) "ip"
    4) "192.168.3.4"
    5) "port"
    6) "6379"
…………………………………………
2)  1) "name"
    2) "192.168.3.3:6379"
    3) "ip"
    4) "192.168.3.3"
    5) "port"
    6) "6379"
…………………………………………

sentinel sentinels <master name>

展示指定 <master name>sentinel节点集合(不包含当前sentinel节点):

127.0.0.1:26380> sentinel sentinels mymaster
1)  1) "name"
    2) "570de1d8085ec8bd7974431c01c589847c857edf"
    3) "ip"
    4) "192.168.3.13"
    5) "port"
    6) "26379"
………………………………………………

sentinel get-master-addr-by-name <master name>

获取主节点信息:

127.0.0.1:26380> sentinel get-master-addr-by-name mymaster
1) "192.168.3.2"
2) "6379"

sentinel failover <master name>

<master name>进行强制故障转移:

127.0.0.1:26380> sentinel failover mymaster
OK
127.0.0.1:26380> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.3.3:6379,slaves=2,sentinels=3

修改配置:

  • 添加新的监听:sentinel monitor test 127.0.0.1 6379 2
  • 放弃对某个master监听:sentinel REMOVE test
  • 设置配置选项:sentinel set failover-timeout mymaster 180000

Master可能会因为某些情况宕机了,如果客户端是固定一个地址去访问,肯定是不合理的,所以客户端请求是请求哨兵,从哨兵获取主机地址的信息,或者是从机的信息。可以实现一个例子:

  • 随机选择一个哨兵连接,获取主机及从机信息。
  • 模拟客户端定时访问,实现简单轮询效果,轮询从节点。
  • 连接失败重试访问

Sentinel故障转移

执行docker-composer up之后sentinel.conf发生了变化,每个配置文件变化如下:

sentinel\conf\sentinel.conf

user default on nopass ~* +@all
sentinel known-replica mymaster 192.168.3.3 6379
sentinel known-replica mymaster 192.168.3.4 6379
sentinel known-sentinel mymaster 192.168.3.12 26379 497f733919cb5d41651b4a2b5648c4adffae0a73
sentinel known-sentinel mymaster 192.168.3.13 26379 0d0ee41bcb5d765e9ff78ed59de66be049a23a82
sentinel current-epoch 0

sentine2\conf\sentinel.conf

user default on nopass ~* +@all
sentinel known-replica mymaster 192.168.3.3 6379
sentinel known-replica mymaster 192.168.3.4 6379
sentinel known-sentinel mymaster 192.168.3.13 26379 0d0ee41bcb5d765e9ff78ed59de66be049a23a82
sentinel known-sentinel mymaster 192.168.3.11 26379 f5f2a73dc0e60514e4f28c6f40517f48fa409eed
sentinel current-epoch 0

sentine3\conf\sentinel.conf

user default on nopass ~* +@all
sentinel known-replica mymaster 192.168.3.3 6379
sentinel known-replica mymaster 192.168.3.4 6379
sentinel known-sentinel mymaster 192.168.3.12 26379 497f733919cb5d41651b4a2b5648c4adffae0a73
sentinel known-sentinel mymaster 192.168.3.11 26379 f5f2a73dc0e60514e4f28c6f40517f48fa409eed
sentinel current-epoch 0

从变化中可以看出每台Sentinel分别记录了slave的节点信息和其它Sentinel节点信息。

在宿主机中随便进入一台Sentinel

127.0.0.1:26380> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "192.168.3.2"
    5) "port"
    6) "6379"

可以观察到监听的所有master,将192.168.3.2这台master进行宕机

docker stop redis-master

宕机完之后等待Sentinel检测周期过了之后再对sentinel.confredis.conf进行观察。

3台Sentinelsentinel monitor mymaster 192.168.3.2 6379 2变成了sentinel monitor mymaster 192.168.3.4 6379 2

其次master对应的slave节点信息也进行更改。

192.168.3.3redis.confreplicaof 192.168.3.2 6379也变成了replicaof 192.168.3.4 6379

192.168.3.2redis.confreplicaof 192.168.3.2 6379这行配置被删除掉了。

再次启动192.168.3.2redis节点,而这台节点的redis.conf中增加了一行replicaof 192.168.3.4 6379

其实就是将我们的操作自动化了。

Sentinel实现原理

Sentinel的实现原理,主要分为三个步骤:

  • 检测问题:三个定时任务,这三个内部的执行任务可以保证出现问题马上让Sentinel知道。
  • 发现问题:主观下线和客观下线,当有一台Sentinel机器发现问题时,它就会对它主观下线。但是当多个Sentinel都发现问题的时候,才会出现客观下线。
  • 找到解决问题的Sentinel:进行领导者选举,如何在Sentinel内部多台节点做领导者选择。
  • 解决问题:就是要进行故障转移。

三个定时任务

  • 每10s每个SentinelMasterSlave执行一次Info Replication

    Redis Sentinel可以对Redis节点做失败判断和故障转移,来Info Replication发现Slave节点,来确定主从关系。

  • 每2s每个Sentinel通过Master节点的channel交换信息(pub/sub)。

    类似于发布订阅,Sentinel会对主从关系进行判断,通过__sentinel__:hello频道交互。了解主从关系可以帮助更好的自动化操作Redis。然后Sentinel会告知系统消息给其它Sentinel节点,最终达到共识,同时Sentinel节点能够互相感知到对方。

  • 每1s每个Sentinel对其它SentinelRedis执行ping

    对每个节点和其它Sentinel进行心跳检测,它是失败判断的依据。

主观下线和客观下线

回顾上一篇文章中Sentinel的配置。

sentinel monitor mymaster 192.168.3.2 6379 2
sentinel down-after-millseconds mymaster 30000

主观下线:每个Sentinel节点对Redis失败的“偏见”。之所以是偏见,只是因为某一台机器30s内没有得到回复。

客观下线:这个时候需要所以Sentinel节点都发现它30s内无回复,才会达到共识。

领导者选举方式

  • 每个做主观下线的Sentinel节点,会像其它的Sentinel节点发送命令,要求将它设置成为领导者。
  • 收到命令的Sentinel节点,如果没有同意通过其它节点发送的命令,那么就会同意请求,否则就会拒绝。
  • 如果Sentinel节点发现自己的票数超过半数,同时也超过了sentinel monitor mymaster 192.168.3.2 6379 2超过2个的时候,就会成为领导者。
  • 进行故障转移操作。

如何选择“合适”的Slave节点

Redis内部其实是有一个优先级配置的,在配置文件中replica-priority,这个参数是slave节点的优先级配置,如果存在则返回,如果不存在则继续。当上面这个优先级不满足的时候,Redis还会选择复制偏移量最大的Slave节点,如果存在则返回,如果不存在则继续。之所以选择偏移量最大,这是因为偏移量越小,和Master的数据越不接近,现在Master挂掉了,说明这个偏移量小的机器数据可能存在问题,这就是为什么选择偏移量最大的Slave的原因。如果发现偏移量都一样,这个时候 Redis 会默认选择 runid 最小的节点。

生产环境部署技巧:

  • Sentinel节点不应该部署在一台物理机器上。

    这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的IP地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现Sentinel节点集合真正的高可用,请勿将Sentinel节点部署在同一台物理机器上。

  • 部署至少三个且奇数个的Sentinel节点。通过增加Sentinel节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加1个节点。

Sentinel常见问题

哨兵集群在发现master node挂掉后会进行故障转移,也就是启动其中一个slave nodemaster node。在这过程中,可能会导致数据丢失的情况。

  • 异步复制导致数据丢失

    因为master->slave的复制是异步,所以有可能部分还没来得及复制到slave就宕机了,此时这些部分数据就丢失了。

  • 集群脑裂导致数据丢失

    脑裂,也就是说。某个master所在机器突然脱离了正常的网络,跟其它slave机器不能连接,但是实际上master还运行着。

造成的问题:

​ 此时哨兵可能就会认为master宕机了,然后开始选举,将其它slave切换成master。这时候集群里就会有2个master,也就是所谓的脑裂。此时虽然某个slave被切换成master,但是可能client还没来得及切换成新的master,还继续写向旧的master的数据可能就丢失了。因此旧master再次被恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会被清空,重新从新的master复制数据。

怎么解决:

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

要求至少有一个slave,数据复制和同步的延迟不能超过10s。

如果说一旦所有的slave,数据复制和同步的延迟都超过了10s,这个时候,master就不会再接收任何请求了。

上面两个配置可以减少异步复制和脑裂导致的数据丢失。

异步复制导致的数据丢失:

​ 在异步复制的过程当中,通过min-slaves-max-lag这个配置,就可以确保的说,一旦slave复制数据和ack延迟时间太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样就可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低到可控范围内。

集群脑裂导致的数据丢失:

​ 集群脑裂因为client还没来得及切换成新的master,还继续写向旧的master的数据可能就丢失了通过min-slaves-to-write确保必须是有多少个从节点连接,并且延迟时间小于min-slaves-max-lag多少秒。

客户端需要怎么做:

​ 对于client来讲,就需要做些处理,比如先将数据缓存到内存当中,然后过一段时间处理,或者连接失败,接收到错误切换新的master处理。