Redis的哨兵

67次阅读

共计 5965 个字符,预计需要花费 15 分钟才能阅读完成。

Redis-Sentinel 简介

Redis Sentinel 是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作(这个选举机制一会介绍),同时会将这个变化实时通知给 Redis 应用方。整个过程完全是 自动的 ,不需要人工来介入,所以这套方案很有效地解决了 Redis 高可用 问题,可以理解为:之前的主从复制中增加了几个哨兵(这里注意是几个而不是一个)来监控 redis,如果主机挂了,哨兵会经过选举在从机中选出一个redis 作为主机,这样就不必手动切换了。

Redis Sentinel 的功能

监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达。
通知:Sentinel 节点会将故障转移的结果通知给应用方。
主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息。
同时 Redis Sentinel 包含了若个 Sentinel 节点,这样做也带来了两个好处:
对于节点的故障判断是由多个 Sentinel 节点共同完成,这样可以有效地防止误判。
Sentinel 节点集合是由若干个 Sentinel 节点组成的,这样即使个别 Sentinel 节点不可用,整个 Sentinel 节点集合依然是健壮的。
Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令

基本的故障转移流程:

1. 主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
2.每个 Sentinel 节点通过定期监控发现主节点出现了故障。
3. 多个 Sentinel 节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。
4.Sentinel 领导者节点执行了故障转移,整个过程基本是跟我们手动调整一致的slaveof no one,只不过是自动化完成的。
5. 故障转移后整个 Redis Sentinel 的结构, 重新选举了新的主节点,并且会通知给客户端。

Sentinel 的配置

  1. sentinel monitor <master-name> <ip> <port> <count>

监控的主节点的 名字 IP 端口 ,最后一个count 的意思是有几台 Sentinel 发现有问题,就会发生故障转移,例如 配置为 2,代表至少有 2 个 Sentinel 节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为 Sentinel 节点的一半加 1
注意:最后的参数不得大于 conut(sentinel)

  1. sentinel down-after-millseconds <master-name> 30000

这个是超时的时间(单位为毫秒)。打个比方,当你去 ping 一个机器的时候,多长时间后仍 ping 不通,那么就认为它是有问题
3.sentinel parallel-syncs <master-name> 1
当 Sentinel 节点集合对主节点故障判定达成一致时,Sentinel 领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数,指出 Sentinel 属于 并发 还是 串行。1 代表每次只能复制一个,可以减轻 Master 的压力;

4.sentinel auth-pass <master-name> <password>

如果 Sentinel 监控的主节点配置了密码,sentinel auth-pass 配置通过添加主节点的密码,防止 Sentinel 节点对主节点无法监控。
5.sentinel failover-timeout mymaster 180000
表示故障转移的时间。
6.sentinel 支持的合法命令如下:
SENTINEL masters 显示被监控的所有 master 以及它们的状态.
SENTINEL master <master name> 显示指定 master 的信息和状态;
SENTINEL slaves <master name> 显示指定 master 的所有 slave 以及它们的状态;
SENTINEL get-master-addr-by-name <master name> 返回指定 master 的 ip 和端口,如果正在进行 failover 或者 failover 已经完成,将会显示被提升为 masterslaveip 和端口。
SENTINEL failover <master name> 强制 sentinel 执行 failover,并且不需要得到其他sentinel 的同意。但是 failover 后会将最新的配置发送给其他sentinel

修改配置
sentinel monitor <master-name> <ip> <port> <count> 添加新的监听

SENTINEL REMOVE <master-name> 放弃对某个 master 监听

SENTINEL set failover-timeout <master-name> 180000 设置配置选项

应用端调用

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

1、随机选择一个哨兵连接,获取主机、从机信息
2、模拟客户端定时访问,实现简单轮训效果,轮训从节点
3、连接失败重试访问
这里我简单写个例子,这里我用了 swoole 的定时器

<?php
class Round{
    static  $lastIndex=0;

    public  function select($list){
        $currentIndex=self::$lastIndex; // 当前的 index
        $value=$list[$currentIndex];
        if($currentIndex+1>count($list)-1){self::$lastIndex=0;}else{self::$lastIndex++;}
        return $value;
    }
}

$sentinelConf=[['ip'=>'xxx','port'=>xxx],
    ['ip'=>'xxx','port'=>xxx],
    ['ip'=>'xxx','port'=>xxx]
];
// 随机访问
$sentinelInfo=$sentinelConf[array_rand($sentinelConf)];

$redis=new Redis();
$redis->connect($sentinelInfo['ip'],$sentinelInfo['port']);
//rawCommand 参数 1 command 2 arguments 3.
$slavesInfo=$redis->rawCommand('SENTINEL','slaves','mymaster');
$slaves=[];
foreach ($slavesInfo as $val){$slaves[]=['ip'=>$val[3],'port'=>$val[5]];
}

// 加载到缓存当中,可以记录这次访问的时间跟上次的访问时间

// 模拟客户端访问
swoole_timer_tick(600,function () use($slaves) {
       // 轮训
        $slave=(new Round())->select($slaves);
        try{$redis=new Redis();
            $redis->connect($slave['ip'],$slave['port']);
            var_dump($slave,$redis->get('username'));
        }catch (\RedisException $e){}});

Sentinel 实现原理三步骤

说完了 Sentinel 的代码实现,很多人对 Sentinel 还不懂其原理。那么接下来就来看下 Sentinel 的实现原理,主要分为以下三个步骤。

1. 检测问题之三个定时任务

10 秒每个 SentinelMasterSlave 执行一次 Info Replication
2 秒每个 Sentinel 通过 Master 节点的 channel 交换信息(pub/sub)。
1 秒每个 Sentinel 对其他 SentinelRedis 执行 ping
第一个定时任务 ,指的是 Redis Sentinel 可以对 Redis 节点做失败判断和故障转移,在 Redis 内部有三个定时任务作为基础,来 Info Replication 发现 Slave 节点,这个命令可以确定主从关系。
第两个定时任务 ,类似于发布订阅,Sentinel 会对主从关系进行判定,通过 _sentinel_:hello 频道交互。了解主从关系可以帮助更好的自动化操作 Redis。然后 Sentinel 会告知系统消息给其它 Sentinel 节点,最终达到共识,同时 Sentinel 节点能够互相感知到对方。
第三个定时任务,指的是对每个节点和其它 Sentinel 进行心跳检测,它是失败判定的依据。

2. 发现问题之主观下线和客观下线

当有一台 Sentinel 机器发现问题时,它就会主观对它主观下线,但是当多个 Sentinel 都发现有问题的时候,才会出现客观下线。
我们先来回顾一下 Sentinel 的配置。

sentinel monitor <master-name>  <ip> <port>   <count>

sentinel down-after-milliseconds <master-name> 30000 

Sentinelping 每个节点,如果超过 30 秒,依然没有回复的话,做下线的判断。

什么是主观下线呢?

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

如何做到客观下线?

这个时候需要所有 Sentinel 节点都发现它 30 秒内无回复,才会达到共识。

3. 找到解决问题的人之领导者选举

1. 每个做主观下线的 sentinel 节点,会向其他的 sentinel 节点发送命令,要求将它设置成为领导者
2. 收到命令sentinel 节点,如果没有同意通过其它节点发送的命令,那么就会同意请求,否则就会拒绝
3. 如果sentinel 节点发现自己票数超过半数,同时也超过了 sentinel monitor <master-name> <ip> <port> <count> 超过count 个的时候,就会成为领导者

4. 解决问题之故障转移

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

生产环境中部署注意事项

1.Sentinel 节点不应该部署在一台物理“机器”上。
这里特意强调物理机是因为一台物理机做成了若干虚拟机或者现今比较流行的容器,它们虽然有不同的 IP 地址,但实际上它们都是同一台物理机,同一台物理机意味着如果这台机器有什么硬件故障,所有的虚拟机都会受到影响,为了实现 Sentinel 节点集合真正的高可用,请勿将 Sentinel 节点部署在同一台物理机器上。
2. 部署至少三个且 奇数 个的 Sentinel 节点。
3. 个以上是通过增加 Sentinel 节点的个数提高对于故障判定的准确性,因为领导者选举需要至少一半加 1 个节点,奇数个节点可以在满足该条件的基础上节省一个节点。

哨兵常见问题

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

1、异步复制导致数据丢失

因为 master->slave 的复制是异步,所以可能有部分还没来得及复制到 slave 就宕机了,此时这些部分数据就丢失了,这个我至今没找到解决的办法,希望有人更正。

2、集群脑裂导致数据丢失

脑裂,也就是说,某个 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

要求至少有 1 个 slave,数据复制和同步的延迟不能超过10
如果说一旦所有的 slave,数据复制和同步的延迟都超过了10 秒钟,那么这个时候,master就不会再接收任何请求了

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

1、异步复制导致的数据丢失

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

2、集群脑裂导致的数据丢失

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

客户端怎么做

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

正文完
 0