基本概念
使用主从复制能够将主节点的数据同步到从节点,一定程度上保证了数据的完整性,也提供了扩展读的能力。
但是主从复制有以下问题:
- 一旦主节点宕机,需要手动提升从节点为主节点
- 主节点的写能力受到单机的限制
- 主节点的存储能力也受到单机的限制
高可用
Redis主从模式下一定程度上解决了高可用问题,但是全程需要人去干预,非常不方便,考虑到这点,有些公司把一些
流程自动化,但是会存在一些问题,比如判断节点不可用的机制是否完善、如何从多个从节点中选出一个晋升为主节点等等,
Redis Sentinel方案正式用于解决这些问题。
当主节点出现故障时,Redis Sentinel能够自动完成故障发现和故障转移,并通知应用方,实现真正的高可用。
Redis Sentinel是一个分布式架构,其中包含多个Sentinel节点和数据节点,每个Sentinel节点都会实时监控数据节点和
其他sentinel节点,并维护它们,整个过程都是自动完成的。
整个过程如下:
- Sentinel发现主节点出现故障
- 多个Sentinel节点对主节点的故障达成一致,选举出某个sentinel节点作为leader负责故障转移
- leader完成故障转移
- 1)提升从节点
- 2)设置之前的从节点设置新的主节点
- 3)通知客户端
- 4)等之前的主节点恢复后,设置为从节点
安装和部署
部署主从节点
redis-server redis-6379.conf
主节点1
2
3
4redis-server redis-6378.conf # 从节点1
slaveof 127.0.0.1 6379
redis-server redis-6380.conf #从节点2
slaveof 127.0.0.1 6379部署Sentinel节点
1
2
3sentinel-26379.conf
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2启动Sentinel节点
两种方式:- 一种是使用redis-server:
redis-server sentinel-26379.conf --sentinel
- 另一种是使用redis-sentinel:
redis-sentinel sentinel-26379.conf
- 一种是使用redis-server:
检查
1
2
3
4
5
6
7
8127.0.0.1:26379> 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=100.101.71.99:6379,slaves=2,sentinels=3重要配置选项
- sentinel monitor {master-name} {ip} {port} {quorum}
sentinel节点会定期监控主节点,quorum代表判定主节点不可用需要的票数,这里只配置了一个ip,是因为,sentinel节点会自动从主节点中获取其他从节点信息。 - sentinel down-after-milliseconds
每个sentinel节点要定期ping命令来判断数据节点和哨兵节点是否可达,超过了这个值,就表示不可达。 - sentinelparallel-syns
这个值表示在故障转移确定了新的主节点后,每次向新的主节点发起复制操作的从节点个数,如果过大,则可能在故障转移时发生阻塞 - sentinel failover-timeout
故障转移的超时时间 - sentinel notification-script
在故障转移期间,当一些警告级别的sentinel事件发生的时候会触发脚本
- sentinel monitor {master-name} {ip} {port} {quorum}
哨兵模式下特殊API
- sentinel master 展示主节点状态及其统计信息
- sentinel slaves {master name} 展示从节点信息
- sentinel reset {pattern} 对符合pattern的主节点的配置进行重置
- sentinel failover{mastername} 对指定主节点进行强制故障转移
- sentinel flushconfig,将哨兵节点的配置刷新到磁盘上,对于磁盘配置文件损坏的情况很有用
- sentinel remove {master-name} 取消该哨兵对于master节点的监控
- sentinel monitor {master-name} {ip}相反的命令
- sentinel set 动态设置相关配置
实现原理
三个定时任务
Redis Sentinel 通过三个定时监控任务完成对各个节点的发现与监控
每隔10秒,每个Sentinel 节点会向主节点和从节点发送info命令获取最新的拓扑结构
1
2
3
4
5$> info replication
role:master
connected_slaves:2
slave1:ip=127.0.0.1,port=6380,state=online,mymaster,offset=0,lag=0
slave2:ip=127.0.0.1,port=6381,state=online,mymaster,offset=0,lag=0该定时任务的作用:
- 通过主节点执行info,获取从节点的信息,所以无需显式配置监控从节点
- 当有新的从节点加入,能够及时感知到
- 节点不可达或者故障转移后,能及时更新拓扑结构
每隔2秒,都会向Redis数据节点的sentinel:hello频道上发送该Sentinel节点对于主节点的判断以及当前sentinel节点的信息
同时sentinel节点也会订阅该频道,互相交换主节点状态,以及版本信息1
2
3
4$:SUBSCRIBE __sentinel__:hello
1) "message"
2) "__sentinel__:hello"
3) "100.101.71.99,26380,13da260e2869a5758a089a2e6fdf50a2e2c23d6e,1,mymaster,100.101.71.99,6380,1该定时任务的作用:
- 发现新的sentinel节点
- 更新配置,如果发现自己的版本低于其他节点的版本,则会更新,有点类似zookeeper的主节点选举,都用到了epoch
每隔1秒每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测
该定时任务的作用是判断节点是否健康的重要依据,也就是ping之后,如果失败则主观下线
主观下线和客观下线
- 主观下线
每隔1秒ping命令做心跳检测时,如果没有在down-after-milliseconds时间内有效回复,则会判定该节点失败。 - 客观下线
当Sentinel节点主观下线的节点是master节点时,该Sentinel节点会通过Sentinel is-master-down-by-addr命令向其他Sentinel节点询问对主节点的判断,
当超过quorum个达成一致时,则会主观下线master节点
Sentinel领导者选举
当Sentinel节点对于master节点做了客观下线后,需要确定哨兵节点的leader,因为故障转移只需要一个Sentinel节点来完成。
选举的过程相对比较简单:
每个Sentinel节点只有一票
每个在线的Sentinel节点都有可能成为leader,当它确认master主观下线时,会想起他Sentinel节点发送成为leader的请求
收到命令的Sentinel节点,如果没有投过票,则会同意该请求,也就是投票
如果该Sentinel节点发现自己的票数大于等于max(quorum,num(sentinels)/2+1),则会成为leader
基于此原则,可以判断谁最先向各个节点发出请求,则最有可能会成为leader
故障转移
- 从follower节点中选出一个可用节点(存活、优先级最高、复制偏移量最大、runid最小(优先级依次降低)的从节点)
- 对从节点执行slaveof no one
- Sentinel节点向其他从节点发送命令,让它们复制新的主节点
- Sentinel节点集合将原来的master节点更新为从节点
运维提示
- 主要分析日志,整个过程都有日志打印
- 需要对主节点下线时,比较合理的做法是使用sentinel failover命令,主动进行故障转移。
- 如果想指定某个slave节点成为master节点,则可以设置其他节点的优先级slave-priority为0,再执行sentinel failover命令,最后将优先级调回来。
config get slave-priority
网络隔离的一致性
如果某个节点上sentinel和redis实例在同一个网络,其他的在另一个网络,当发送网络中断时会发生意外:
1 | +-------------+ |
Redis3一开始是master,网络断开后,Redis1成为了新的主节点,Sentinel1和Sentinel2更新了配置,但这是Sentinel3还是原来的配置,这个时候我们出现了常说的脑裂现象(分布式常见的问题),ClientB仍然向Redis3写数据。
当网络恢复后,Sentinel3恢复更新配置,这时Redis3成为了slave,之前ClientB写入的数据将会丢失。
如果把redis当成缓存,也许可以容忍,但如果是一个存储系统,则无法容忍。
如果看过zookeeper的源码分析就知道,zookeeper有机制防止脑裂现象,它是通过持续交换信息,大于一般才正常工作,但这里的redis不是,为什么redis像zookeeper那样,保证
节点只有在超过半数以上才可用呢?我个人感觉根据cpa和base理论,Redis为了保证可用性,使用的是最终一致性,牺牲了高度一致性,尤其是其异步机制,也就是PA+最终一致性。另外在网络发生分区时A和P基本不可实现。也就是说当网络分区发生时只有PA和PC,由于被断开网络的那小部分不可用(如下文所说的配置)又在一定程度上变成了PC。
另外在分布式系统中,只要是不是在一个节点上部署,那么分区的情况将不可避免,所以如果满足CA,当数据分区时则最终会退化为只有A或者C,所以系统一般做成PA或者PC,而不是CA。
解决方法:
保证主节点至少有一个slave节点,但因为复制过程是异步的,所以需要两个配置:
1 | min-replicas-to-write 1 |
min-replicas-to-write保证了至少一个slave节点
min-replicas-max-lag,保证了slave节点复制同步最多10秒的延迟,如果10秒内没有同步成功,则代表slave失败。
但还是有10秒的数据丢失