`Sentinel`服务本身也是一个特殊的`Redis`服务,在`Redis`的安装包内,除了`redis.conf`文件,还有一个`sentinel.conf`文件,这个就是启动`sentinel`服务的配置文件。
启动`Sentinel`服务的命令通过`redis-sentinel`来执行(如:`./redis-sentinel ../sentinel.conf`)或者也可以通过`redis-server`命令指定参数`sentinel`来启动(如:`./redis-server ../sentinel.conf --sentinel`)。
`Sentinel`主要用来监控`Redis`集群中的所有节点,当`Sentinel`服务发现`master`不可用时,可以从所有可用的`slave`节点中选出一个新的节点升级为`master`,从而实现`master`服务的自动切换。
如果`Sentinel`服务自己挂了怎么办?为了实现高可用,`Sentinel`服务本身也是一个集群。和`master-slave`模式不同的是,`Sentinel`集群之间在正常情况下没有主从关系,相互之间是平等的,只有在需要执行故障转移时才需要进行`Leader`选举。
下图所示就是一个`3`节点`Sentinel`服务集群和`1`主`2`从的`Redis Sentinel`集群示意图:
![](https://img.kancloud.cn/a1/4b/a14bf480f86e28ab63a08c9f2ff46f77_608x351.png)
`Sentinel`集群之间的服务会互相监控,然后每个`Sentinel`服务都会监控所有的`master`和`slave`节点,一旦发现`master`节点不可用,则`Sentinel`集群会通过选举产生`Leader`,再由`Leader`节点执行故障转移,切换`master`节点。
#### 主观下线和客观下线
`Sentinel`服务默认以每秒`1`次的频率向`Redis`服务节点发送`ping`命令(`Sentinel`服务之间也会发送`ping`命令进行检测)。如果在指定时间内(由参数`down-after-milliseconds`进行控制,默认`30s`)没有收到有效回复,则`Sentinel`会将该服务器标记为下线,称之为:**主观下线**。
~~~
down-after-milliseconds master-name milliseconds #判断主观下线超时时间
~~~
当某一个`Sentinel`服务把`master`节点标记为主观下线之后,会去询问其它`Sentinel`服务,确认这个`master`是否真的下线,当达到指定数量的`Sentinel`服务都认为当前`master`节点已经主观下线,这时候首先发现`master`节点主观下线的`Sentinel`服务就会将`master`节点标记为**客观下线**,并发起`Leader`选举,最后由`Leader`执行**故障转移**操作。
多少个`Sentinel`服务认定`master`节点“主观下线”才会正式将`master`节点标记为“客观下线”由以下参数控制:
~~~
sentinel monitor <master-name> <ip> <redis-port> <quorum> #quorum 选项就是决定判断客观下线的条数
~~~
需要注意的是,每个`Sentinel`服务判断主观下线和客观下线的配置可能不一样,所以当某一个`Sentinel`服务判定`master`已经主观下线或者客观下线时,其它`Sentinel`服务并不一定会这么认为,但是只要有一个`Sentinel`判定`master`已经客观下线,`Sentinel`就会准备执行故障转移。在执行故障转移之前,`Sentinel`服务之间必须进行`Leader`选举。
#### Leader 选举
当某一个或者多个`Sentinel`服务判定`master`服务已经下线,`Sentinel`服务就会发起`Leader`选举,选举出`Leader`之后,由`Leader`节点执行故障转移。
##### Raft 选举算法
`Sentinel`服务的`Leader`选举是通过`Raft`算法来实现的。`Raft`是一个共识算法(consensus algorithm),其核心思想主要有两点:
* 先到先得
* 少数服从多数
在`Raft`算法中,每个节点都维护了一个属性`election timeout`,这是一个随机的时间,范围在`150ms~300ms`之间,哪个节点先到达这个时间,哪个节点就可以发起选举投票。
选举过程可以总结为以下步骤:
1. 发起选举的服务首先会给自己投上一票。
2. 然后发起选举的节点会向其它节点发送投票请求,其它节点在收到请求后如果在同一个`election timeout`范围内还没有投过票,那么就会给发起选举的节点投上一票,然后将`election timeout`重置(一个`election timeout`区间只能发起一次投票)。
3. 如果发起选举的节点获得的票数超过一半,那么当前服务就会成为`Leader`节点,成为`Leader`节点之后就会维护一个`heartbeat timeout`时间属性(即:心跳间隔时间),在每一次到达`heartbeat timeout`时间时,`Leader`节点就会向其它`Follow`节点发起一个心跳检测。
4. `Follow`节点收到`Leader`节点的心跳包之后就会将`election timeout`清空,这样可以防止`Follow`节点因为到达`election timeout`而发起选举。
5. 假如`Leader`节点挂了,那么`Follow`节点的`election timeout`将不会被清空,谁先到达,谁就会再次发起选举。
PS:因为`election timeout`是一个随机值,虽然概率小,但也可能出现两个节点同时发起投票选举,这种情况就可能出现一次选举并不能选出`Leader`(比如总共`4`个节点,每个节点都得了`2`票),此时就会等待下一次首先到达`election timeout`的节点再次发起投票选举。
如果对`Raft`算法感兴趣的,可以[点击这里](http://thesecretlivesofdata.com/raft/)观看演示。
##### Sentinel 选举 Leader
`Sentinel`中的选举算法虽然是源于`Raft`算法,但是也做了以下改进:
1. 触发选举并不是由`election timeout`时间决定,而是由谁先判定`master`下线来决定的。
2. `Sentinel`节点并没有维护`election timeout`属性,而是维护了一个配置纪元`configuration epoch`属性,配置纪元是一个计数器(默认`0`),每一个`Sentinel`节点的同一个配置纪元只能投票`1`次(先到先得),每次投票前会将配置纪元自增`1`。
3. 选举出`Leader`之后,`Leader`并不会通知其它`Follow`节点自己成为了`Leader`。当`Leader`节点选出新的`master`,其它`Sentinel`服务检测到新的`master`上线之后,就会删除自己的主观下线标记。
#### 故障转移
当`Sentinel`选举出`Leader`之后,`Leader`就会开始执行故障转移,执行故障转移主要分为以下三步:
1. 在已判定客观下线的`master`服务器的`slave`服务器列表中找到一个合格的`slave`服务器,向其发送`replicaof no one`命令,使其转换为`master`角色。
2. 向其它从服务器发送`replicaof ip port`命令(`ip`和`port`为新`master`地址),使其成为新`master`服务的`slave`节点。
3. 将已下线的`master`服务也设置为新的`master`服务的`slave`节点,这样当旧`master`恢复之后能以`slave`的角色继续运行。
##### 如何选举新的 master 节点
新的`master`选举条件主要参考`4`个因素:
1. 断开连接时长:首先将所有于已下线`master`节点断开连接时间超过`down-after-milliseconds * 10`的`slave`节点删除掉,确保`slave`节点的数据都是比较新的。
2. `slave`节点的优先级排序:将所有的`slave`节点按照优先级进行排序,选出优先级最高的`slave`节点作为新的`master`节点(优先级由配置文件参数`replica-priority`决定,默认`100`)。
3. 复制偏移量:如果有多个优先级相同的`slave`节点,则选出复制偏移量最大的`slave`节点。
4. 进程`id`:如果还是没选出新的`master`节点,那么会再次选择进程`id`最小的`slave`节点作为新的`master`节点。
- Redis 为什么这么快
- 什么是 Redis
- Redis 的安装
- Redis 到底有多快
- Redis 是单线程还是多线程
- Redis 为什么选择使用单线程来执行请求
- 什么是 IO 多路复用机制
- Redis 中 I/O 多路复用的应用
- 一个简单的字符串,为什么 Redis 要设计的如此特别
- Redis 的 9 种数据类型
- 二进制安全字符串
- sds 空间分配策略
- sds 和 C 语言字符串区别
- sds 是如何被存储的
- type 属性
- encoding 属性
- 通过牺牲速度来节省内存,Redis 是觉得自己太快了吗
- 什么是压缩列表
- ziplist 的存储结构
- entry 存储结构
- ziplist 数据示例
- ziplist 连锁更新问题
- 为了加快速度,Redis 都做了哪些“变态”设计
- 列表对象
- linkedlist
- linkedlist 和 ziplist 的选择
- quicklist
- 列表对象常用操作命令
- Redis 中哈希分布不均匀该怎么办
- 哈希对象
- hashtable
- ziplist
- ziplist 和 hashtable 的编码转换
- 哈希对象常用命令
- 同一份数据,Redis 为什么要存”两次”
- 五种基本类型之集合对象
- intset 编码
- 集合对象常用命令
- 五种基本类型之有序集合对象
- skiplist 编码
- ziplist 编码
- ziplist 和 skiplist 编码转换
- 有序集合对象常用命令
- 要想用活 Redis,Lua 脚本是绕不过去的坎
- 发布与订阅
- 基于频道的实现
- 基于模式的实现
- Lua 脚本
- Lua 脚本的调用
- Lua 脚本中执行 Redis 命令
- Lua 脚本摘要
- Lua 脚本文件
- 脚本异常
- 作为一款内存数据库,为什么断电后 Redis 数据不会丢失
- Redis 持久化机制
- RDB 持久化机制
- AOF 持久化机制
- 内存耗尽后 Redis 会发生什么
- 内存回收
- 过期策略
- 8 种淘汰策略
- LRU 算法
- LFU 算法
- 不能回滚的 Redis 事务还能用吗
- Redis 有事务吗
- Redis 事务实现原理
- Redis 事务 ACID 特性
- watch 命令
- watch 命令的作用
- watch 原理分析
- Redis 为什么不直接用 master-slave 集群
- Redis 集群方案
- 主从复制
- 配置一主两从 master-slave 集群
- 主从复制原理分析
- 主从服务的不足之处
- Sentinel(哨兵)机制为什么从神坛滑落
- 哨兵 Sentinel 机制
- Sentinel 原理分析
- 配置 Sentinel 集群
- Sentinel 机制实战
- Sentinel 机制的不足之处
- Redis Cluster 集群凭什么成为了最终的胜利者
- Redis 分布式集群方案
- 客户端实现分片
- 中间代理服务实现分片
- Redis Cluster 方案
- 手动配置一个 Redis Cluster 集群
- Redis Cluster 集群常用命令
- 客户端如何使用 Redis Cluster 集群
- Redis Cluster 的不足
- 如何从 10 亿数据中快速判断是否存在某一个元素
- 缓存雪崩
- 缓存击穿
- 缓存穿透
- 布隆过滤器(Bloom Filter)
- 布隆过滤器的 2 大特点
- 布隆过滤器的实现(Guava)
- 布隆过滤器的如何删除