# Redis-Sentinel简介
[Redis Sentinel](https://redis.io/topics/sentinel)是一个分布式架构,其中包含若干个 `Sentinel` 节点和 `Redis` 数据节点,每个 `Sentinel` 节点会对数据节点和其余 `Sentinel` 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 `Sentinel` 节点进行“协商”,当大多数 `Sentinel` 节点都认为主节点不可达时,它们会选举出一个 `Sentinel` 节点来完成自动故障转移的工作(这个选举机制一会介绍),同时会将这个变化实时通知给 `Redis` 应用方。整个过程完全是`自动的`,不需要人工来介入,所以这套方案很有效地解决了 `Redis` 的`高可用`问题,可以理解为:之前的主从复制中增加了几个哨兵(这里注意是几个而不是一个)来监控`redis`,如果主机挂了,哨兵会经过选举在从机中选出一个`redis`作为主机,这样就不必手动切换了。
![](https://img.kancloud.cn/30/c8/30c8d4afa40fbcdc15d7bb63899ad29f_800x434.png)
# Redis Sentinel 的功能
`1·`监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达。
`2·`通知:Sentinel 节点会将故障转移的结果通知给应用方。
`3·`主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
`4·`配置提供者:在 `Redis Sentinel` 结构中,客户端在初始化的时候连接的是 `Sentinel` 节点集合,从中获取主节点信息。
同时`Redis Sentinel` 包含了若个 `Sentinel` 节点,这样做也带来了两个好处:
`5·`对于节点的故障判断是由多个 `Sentinel` 节点共同完成,这样可以有效地防止误判。
`6·`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)**
2. `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`已经完成,将会显示被提升为`master`的`slave`的`ip`和端口。
`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
<?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`秒每个 `Sentinel` 对 `Master` 和 `Slave` 执行一次 `Info Replication`。
每`2`秒每个 `Sentinel` 通过 `Master` 节点的 `channel` 交换信息(`pub/sub`)。
每`1`秒每个 `Sentinel` 对其他 `Sentinel` 和 `Redis` 执行 `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
`Sentinel` 会 `ping` 每个节点,如果超过`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 node`为`master 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`处理。
- 微服务
- 服务器相关
- 操作系统
- 极客时间操作系统实战笔记
- 01 程序的运行过程:从代码到机器运行
- 02 几行汇编几行C:实现一个最简单的内核
- 03 黑盒之中有什么:内核结构与设计
- Rust
- 入门:Rust开发一个简单的web服务器
- Rust的引用和租借
- 函数与函数指针
- Rust中如何面向对象编程
- 构建单线程web服务器
- 在服务器中增加线程池提高吞吐
- Java
- 并发编程
- 并发基础
- 1.创建并启动线程
- 2.java线程生命周期以及start源码剖析
- 3.采用多线程模拟银行排队叫号
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的应用分析
- 6.Daemon线程的创建以及使用场景分析
- 7.线程ID,优先级
- 8.Thread的join方法
- 9.Thread中断Interrupt方法学习&采用优雅的方式结束线程生命周期
- 10.编写ThreadService实现暴力结束线程
- 11.线程同步问题以及synchronized的引入
- 12.同步代码块以及同步方法之间的区别和关系
- 13.通过实验分析This锁和Class锁的存在
- 14.多线程死锁分析以及案例介绍
- 15.线程间通信快速入门,使用wait和notify进行线程间的数据通信
- 16.多Product多Consumer之间的通讯导致出现程序假死的原因分析
- 17.使用notifyAll完善多线程下的生产者消费者模型
- 18.wait和sleep的本质区别
- 19.完善数据采集程序
- 20.如何实现一个自己的显式锁Lock
- 21.addShutdownHook给你的程序注入钩子
- 22.如何捕获线程运行期间的异常
- 23.ThreadGroup API介绍
- 24.线程池原理与自定义线程池一
- 25.给线程池增加拒绝策略以及停止方法
- 26.给线程池增加自动扩充,闲时自动回收线程的功能
- JVM
- C&C++
- GDB调试工具笔记
- C&C++基础
- 一个例子理解C语言数据类型的本质
- 字节顺序-大小端模式
- Php
- Php源码阅读笔记
- Swoole相关
- Swoole基础
- php的五种运行模式
- FPM模式的生命周期
- OSI网络七层图片速查
- IP/TCP/UPD/HTTP
- swoole源代码编译安装
- 安全相关
- MySql
- Mysql基础
- 1.事务与锁
- 2.事务隔离级别与IO的关系
- 3.mysql锁机制与结构
- 4.mysql结构与sql执行
- 5.mysql物理文件
- 6.mysql性能问题
- Docker&K8s
- Docker安装java8
- Redis
- 分布式部署相关
- Redis的主从复制
- Redis的哨兵
- redis-Cluster分区方案&应用场景
- redis-Cluster哈希虚拟槽&简单搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸缩调优
- 源码阅读笔记
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的东西
- 一次折腾了几天的大华摄像头调试经历
- 搬砖实用代码
- python读取excel拼接sql
- mysql大批量插入数据四种方法
- composer好用的镜像源
- ab
- 环境搭建与配置
- face_recognition本地调试笔记
- 虚拟机配置静态ip
- Centos7 Init Shell
- 发布自己的Composer包
- git推送一直失败怎么办
- Beyond Compare过期解决办法
- 我的Navicat for Mysql
- 小错误解决办法
- CLoin报错CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM无法连接到虚拟机
- Jetbrains相关
- IntelliJ IDEA 笔记
- CLoin的配置与使用
- PhpStormDocker环境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer