官方文档
https://redis.io/topics/sentinel
简介
Redis Sentinel为Redis提供高可用性Sentinel模式是一种特殊的模式,首先Redis提供了Sentinel的命令,Sentinel是一个独立的进程,作为进程,它会独立运行其原理是Sentinel通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
作用
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器
- 通知Sentinel可以通过API通知系统管理员,另一台计算机程序,其中一个受监控的Redis实例出现问题
- 当Sentinel监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机
- Configuration provider客户端连接Sentinel可以获得当前Redis主服务器的地址等
结构图
![c3882b60e88064d396f7202c1bf71e66.png](https://i-blog.csdnimg.cn/blog_migrate/f58d1c55e3e6c70769a22b8a949fb391.jpeg)
名词解释
- quorum:确认odown的最少的Sentinel数量
- majority:授权进行主从切换的最少的Sentinel数量
- failover:Sentinel确认odown进行故障转移的过程
- odown:多个 Sentinel 实例在对Master Server做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,然后开启failover.
- sdown:当前 Sentinel 实例对某个redis服务器做出的下线判断
docker实例
拉取镜像docker pull redis
master配置 折叠原码# 指定日志文件
logfile
"redis.log"
# 指定端口
port
6380
# 指定数据目录,rdb、aof文件也会写在这个目录
dir /data
# 以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的
# AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
# 理解掌握好AOF持久化机制对兼顾数据安全性和性能非常有帮助通俗一点的理解就是以日志
# 的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加
# 文件但不可以改写文件,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
appendonly yes
# 指定AOF
appendfilename appendonly.aof
# master设置的连接验证 从库要用masterauth来进行连接
requirepass
1234
# slave连接master验证 都加上此配置,当完成failover后仍然保持master需要验证
masterauth
1234
# slave1
# 指定日志文件
logfile
"redis.log"
# 指定端口
port
6381
# 指定数据目录,rdb、aof文件也会写在这个目录
dir /data
# 是否开启AOF模式
appendonly yes
# 指定AOF
appendfilename appendonly.aof
# 指定master
slaveof
172.16.81.232
6380
# master设置的连接验证 从库要用masterauth来进行连接
requirepass
1234
# slave连接master验证 都加上此配置,当完成failover后仍然保持master需要验证
masterauth
1234
# slave2
# 指定日志文件
logfile
"redis.log"
# 指定端口
port
6382
# 指定数据目录,rdb、aof文件也会写在这个目录
dir /data
# 是否开启AOF模式
appendonly yes
# 指定AOF
appendfilename appendonly.aof
# 指定master
slaveof
172.16.81.232
6380# master设置的连接验证 从库要用masterauth来进行连接
requirepass
1234
# slave连接master验证 都加上此配置,当完成failover后仍然保持master需要验证
masterauth
1234
启动容器docker run -it --name redis-master -p
6380:6380
-v /Users/zhudd/redisTest/6380/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -it --name redis-slave1 -p
6381:6381
-v /Users/zhudd/redisTest/6381/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
docker run -it --name redis-slave2 -p
6382:6382
-v /Users/zhudd/redisTest/6382/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf
查看容器运行状态
docker ps
检查主从同步配置
docker exec -it c0 bash
root@c0b98619a2da:/data# redis-cli -p
6380
-a
1234
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.1,port=6381,state=online,offset=812,lag=0
slave1:ip=172.17.0.1,port=6382,state=online,offset=812,lag=0
master_replid:828fe844d602a47a942d183d0906966ba223c059
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:812
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:812
从库
# Replication
role:slave
master_host:172.16.81.232
master_port:6380
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:6916
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:828fe844d602a47a942d183d0906966ba223c059
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:6916
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:6916
检查主从同步配置 折叠原码
docker exec -it c0 bash
root@c0b98619a2da:/data# redis-cli -p
6380
-a
1234
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.1,port=6381,state=online,offset=812,lag=0
slave1:ip=172.17.0.1,port=6382,state=online,offset=812,lag=0
master_replid:828fe844d602a47a942d183d0906966ba223c059
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:812
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:812
从库
# Replication
role:slave
master_host:172.16.81.232
master_port:6380
master_link_status:up
master_last_io_seconds_ago:10
master_sync_in_progress:0
slave_repl_offset:6916
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:828fe844d602a47a942d183d0906966ba223c059
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:6916
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:6916
sentinel配置 折叠原码# sentinal1
port
26380
dir /data
logfile
"sentinel.log"
# 指定要监视的redis实例 当2个sentinel认为master失效时 会开始failover流程
sentinel monitor mymaster
172.16.81.232
6380
2
# 当sentinel认为实例失效的时间(ms)
sentinel down-after-milliseconds mymaster
10000
# failover超时时间在(ms)内没完成,则 不会进行failover
sentinel failover-timeout mymaster
60000
# sentinel连接需要验证的master需要配置
sentinel auth-pass mymaster
123456
# sentinal2
port
26381
dir /data
logfile
"sentinel.log"
# 指定要监视的redis实例 当2个sentinel认为master失效时 会开始failover流程
sentinel monitor mymaster
172.16.81.232
6380
2
# 当sentinel认为实例失效的时间(ms)
sentinel down-after-milliseconds mymaster
10000
# failover超时时间在(ms)内没完成,则 不会进行failover
sentinel failover-timeout mymaster
60000
# sentinel连接需要验证的master需要配置
sentinel auth-pass mymaster
123456
# sentinal3
port 26382dir /data
logfile
"sentinel.log"
# 指定要监视的redis实例 当2个sentinel认为master失效时 会开始failover流程
sentinel monitor mymaster
172.16.81.232
6380
2
# 当sentinel认为实例失效的时间(ms)
sentinel down-after-milliseconds mymaster
10000
# failover超时时间在(ms)内没完成,则 不会进行failover
sentinel failover-timeout mymaster
60000
# sentinel连接需要验证的master需要配置
sentinel auth-pass mymaster
123456
sentinel状态 折叠原码dk exec -it b0 bash
root@b0032eb467d6:/data# redis-cli -p
26382
127.0.0.1:26382> 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=172.16.81.232:6380,slaves=2,Sentinels=3
# 查看master的地址
127.0.0.1:26382> Sentinel get-master-addr-by-name mymaster
1)
"172.16.81.232"
2)
"6380"
查看Sentinel日志,可以发现两个slave和其他两个Sentinel已经被发现:
![d575993e0692d8b186a921e2b67b1930.png](https://i-blog.csdnimg.cn/blog_migrate/9ecf1c94ecb599547b45fadc8ae1d1eb.jpeg)
测试阶段
- 测试断掉master,是否会按照预期的发生failover,重新选择master:停止现有master容器
docker stop c0
发生了failover,重新选择master:(虽然6380的master没有运行,但还是被添加为了slave)failover1:X
15
Apr
2019
11:49:53.671
# +sdown master mymaster
172.16.81.232
6380
1:X
15
Apr
2019
11:49:53.731
# +new-epoch
1
1:X
15
Apr
2019
11:49:53.737
# +vote-for-leader 2a6578438619d67a76a817c0f308231579f1d4f4
1
1:X
15
Apr
2019
11:49:53.737
# +odown master mymaster
172.16.81.232
6380
#quorum
3/2
1:X
15
Apr
2019
11:49:53.737
# Next failover delay: I will not start a failover before Mon Apr
15
11:51:54
2019
1:X
15
Apr
2019
11:49:54.558
# +config-update-from Sentinel 2a6578438619d67a76a817c0f308231579f1d4f4
172.17.0.7
26382
@ mymaster
172.16.81.232
6380
1:X
15
Apr
2019
11:49:54.558
# +switch-master mymaster
172.16.81.232
6380
172.17.0.1
6382
1:X
15
Apr
2019
11:49:54.559
* +slave slave
172.17.0.1:6381
172.17.0.1
6381
@ mymaster
172.17.0.1
6382
1:X
15
Apr
2019
11:49:54.559
* +slave slave
172.16.81.232:6380
172.16.81.232
6380
@ mymaster
172.17.0.1
6382
1:X
15
Apr
2019
11:50:04.578
# +sdown slave
172.16.81.232:6380
172.16.81.232
6380
@ mymaster
172.17.0.1
![e6a210c74dff39a13504fd31f7b49209.png](https://i-blog.csdnimg.cn/blog_migrate/f3e4e39bdec6f311a11c923423c70247.jpeg)
原master配置文件已经被重新
![e7c00cacb832d188401a3fa7b137216a.png](https://i-blog.csdnimg.cn/blog_migrate/bf02b47a484f47b890b28dc6a80130db.jpeg)
重新添加master,此master还是会被当做slave,不会重新作为master:
启动原masterdocker start c0
添加slave1:X
15
Apr
2019
12:03:37.330
# -sdown slave
172.16.81.232:6380
172.16.81.232
6380
@ mymaster
172.17.0.1
6382
1:X
15
Apr
2019
12:03:48.486
* +slave slave
172.17.0.1:6380
172.17.0.1
6380
@ mymaster
172.17.0.1
![58423caf550f726be5c663706984e652.png](https://i-blog.csdnimg.cn/blog_migrate/f1f2bc021bf7460ef8c934af2fc01867.jpeg)
Sentinel之间也会互相监视,对其他Sentinel重启:
sentinel感知1:X
15
Apr
2019
12:19:40.861
# +sdown Sentinel 58c20f1d11bcbd68b927fca77c7b85dafda565ba
172.17.0.6
26381
@ mymaster
172.17.0.1
6382
1:X
15
Apr
2019
12:20:25.295
# -sdown Sentinel 58c20f1d11bcbd68b927fca77c7b85dafda565ba
172.17.0.626381@ mymaster172.17.0.16382
Sentinel特性
- 故障转移时,判断一个master node是宕机了,需要大部分的Sentinel都同意才行,涉及到了分布式选举的问题
- Sentinel至少需要3个实例,来保证自己的健壮性
- Sentinel + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
sdown和odown
- sdown和odown两种失败状态
- sdown是主观宕机,就一个Sentinel如果自己觉得一个master宕机了,那么就是主观宕机
- odown是客观宕机,如果quorum数量的Sentinel都觉得一个master宕机了,那么就是客观宕机
- sdown达成的条件:如果一个Sentinelping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机
- odown达成的条件:如果一个Sentinel在指定时间内,收到了quorum指定数量的其他Sentinel也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机
quorum和majority
- quorum:确认odown的最少的Sentinel数量
- majority:授权进行主从切换的最少的Sentinel数量
- 每次一个Sentinel要做主备切换,首先需要quorum数量的Sentinel认为odown,然后选举出一个Sentinel来做切换,这个Sentinel还得得到majority Sentinel的授权,才能正式执行切换
- 如果quorum < majority,比如5个Sentinel,majority就是3,quorum设置为2,那么就3个Sentinel授权就可以执行切换,但是如果quorum >= majority,那么必须quorum数量的Sentinel都授权,比如5个Sentinel,quorum是5,那么必须5个Sentinel都同意授权,才能执行切换
master选举算法
如果一个master被认为odown了,而且majoritySentinel都允许了主备切换,那么某个Sentinel就会执行主备切换操作,此时首先要选举一个slave来
选举的时候会考虑slave的一些信息:
- 跟master断开连接的时长
- slave优先级
- 复制offset
- run id
如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master,计算公式如下:(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下来会对slave进行排序
(1)按照slave优先级进行排序,slave priority越低,优先级就越高
(2)如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高
(3)如果上面两个条件都相同,那么选择一个run id比较小的那个slaveSentinel leader选举算法
参考:Raft协议,有Leader, Follower, Candidate三种角色
- 1、某个Sentinel认定master sdown后,该Sentinel会先检查自己有没有投过票,如果自己已经投过票给其他Sentinel了,在2倍failover的超时时间自己就不会成为Leader相当于它是一个Follower
- 2、如果该Sentinel还没投过票,那么它就成为Candidate
- 3、和Raft协议描述的一样,成为Candidate,Sentinel需要完成几件事情
- 1)更新failover状态为start
- 2)当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term
- 3)更新自己的超时时间为当前时间随机加上一段时间,随机时间为1s内的随机毫秒数
- 4)向其他节点发送
is-master-down-by-addr
命令请求投票命令会带上自己的epoch - 5)给自己投一票,在Sentinel中,投票的方式是把自己master结构体里的leader和leader_epoch改成投给的Sentinel和它的epoch
- 4、其他Sentinel会收到Candidate的
is-master-down-by-addr
命令如果Sentinel当前epoch和Candidate传给他的epoch一样,说明他已经把自己master结构体里的leader和leader_epoch改成其他Candidate,相当于把票投给了其他Candidate投过票给别的Sentinel后,在当前epoch内自己就只能成为Follower - 5、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum
- 6、如果在一个选举时间内,Candidate没有获得超过一半且超过它配置的quorum的票数,自己的这次选举就失败了
- 7、如果在一个epoch内,没有一个Candidate获得更多的票数那么等待超过2倍故障转移的超时时间后,Candidate增加epoch重新投票
- 8、如果某个Candidate获得超过一半且超过它配置的quorum的票数,那么它就成为了Leader
- 9、与Raft协议不同,Leader并不会把自己成为Leader的消息发给其他Sentinel其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程
configuration epoch
Sentinel会对一套redis master+slave进行监控,有相应的监控的配置
执行切换的那个Sentinel,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的
如果第一个选举出的Sentinel切换失败了,那么其他Sentinel,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号configuraiton传播
Sentinel完成切换之后,会在自己本Sentinel地更新生成最新的master配置,然后同步给其他的Sentinel,就是通过之前说的pub/sub消息机制
这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个Sentinel完成一次新的切换之后,新的master配置是跟着新的version号的
其他的Sentinel都是根据版本号的大小来更新自己的master配置的