复制积压缓冲区
上面的部分重同步貌似看起来能解决问题,但是这又会带来另一个问题,那就是当主服务器将命令发送出去之后,为了实现部分重同步还需要将命令保存起来,否则当从服务器的偏移量低于主服务器时,主服务器也无法将命令重传播。
那么问题就来了,这个命令要保存多久呢?如果一直保存下去就会占据大量的空间,为了解决这个问题,master服务器维护了一个固定长度的FIFO队列,即复制积压缓冲区。
当进行命令传播的过程中,master服务器不仅会将命令传播给所有的slave服务器,同时还会将命令写入复制积压缓冲区。复制积压缓冲区默认大小为1MB。
下面就是一个完整的部分重同步流程图:
也就是说,当master服务器记录的偏移量+1已经不存在与复制积压缓冲区了,就会执行一次全量同步,即发送RDB文件给从服务器。
主从服务的不足之处
主从服务器通过读写分离实现了数据的可靠性,但是其并未实现系统的高可用性。其主要存在以下两个问题:
-
1、首次同步或者部分重同步时需要执行全量同步时发送的RDB文件如果过大,则会非常耗时。
-
2、假如master服务器挂了,那么系统并不能手动切换master服务器,仍然需要人为进行切换。
Redis的Sentinel机制主要是为了实现Redis服务器的高可用性而设计的一种解决方案。这种方案也是为了弥补主从复制模式的不足,Sentinel机制实现了主从服务的自动切换。
Sentinel原理分析
Sentinel其本身也是一个特殊的Redis服务,在Redis的安装包内,除了redis.conf文件,还有一个sentinel.conf文件,这个就是启动sentinel服务的配置文件,启动命令则通过redis-sentinel
来执行(如:./redis-sentinel ../sentinel.conf
)或者也可以通过redis-server
命令指定参数sentinel
来启动(如:./redis-server ../sentinel.conf --sentinel
)。
Sentinel主要用来监控Redis集群中的所有节点,当Sentinel服务发现master不可用时,可以实现master服务的自动切换。但是如果Sentinel服务自己挂了怎么办?所以为了实现高可用,Sentinel服务本身也是一个集群,和Redis的master-slave模式不同的是,Sentinel集群之间在正常情况下没有主从关系,相互之间是平等的,只有在需要执行故障转移时才需要进行Leader选举。
下图就是一个3个Sentinel服务集群和1主2从的Redis集群示意图:
Sentinel集群之间的服务会互相监控,然后每个Sentinel服务都会监控所有的master-slave节点,一旦发现master节点不可用,则Sentinel中通过选举产生的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服务器已经主观下线,这时候Sentinel就会将master服务标记为客观下线,并执行故障转移操作。
多少个Sentinel服务认定master节点主观下线才会正式将master服务标记为客观下线,由以下参数控制:
sentinel monitor
其中的quorum
选项就是决定了这个数量。
需要注意的是,每个Sentinel服务的判断主观下线和客观下线的配置可能不一样,所以当Sentinel1判定master已经主观下线或者客观下线时,其他Sentinel服务并不一定会这么认为,但是只要有一个Sentinel判定master已经客观下线,其就会执行故障转移,但是故障转移并不一定是由判断为客观下线的Sentinel服务来执行,在执行故障转移的之前,Sentinel服务之间必须进行Leader选举。
Leader选举
当某一个或者多个Sentinel服务判定master服务已经下线,其会发起Leader选举,选举出Leader之后,由Leader节点执行故障转移。
Raft选举算法
Sentinel服务的Leader选举是通过Raft算法来实现的。Raft是一个共识算法(consensus algorithm),其核心思想主要有两点:
-
1、先到先得
-
2、少数服从多数
在Raft算法中,每个节点都维护了一个属性election timeout
,这是一个随机的时间,范围在150ms~300ms之间,哪个节点先到达这个时间,哪个节点就可以发起选举投票。
选举步骤总要可以总结为以下步骤:
-
1、发起选举的服务首先会给自己投上一票。
-
2、然后会向其他节点发送投票请求到其他节点,其他节点在收到请求后如果在
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算法感兴趣的,可以点击这里观看演示。
Sentinel选举Leader
Sentinel中的选举虽然是源于Raft算法,但是也做了以下改进:
-
1、触发选举并不是由
election timeout
时间决定,而是由谁先判定master下线来决定的。 -
2、Sentinel节点并没有维护
election timeout
属性,而是维护了一个配置纪元configuration epoch
属性,配置纪元是一个计数器(默认0),每一个Sentinel节点的同一个配置纪元只能投票1次(先到先得),每次投票前会将配置纪元自增+1。 -
3、选举出Leader之后,Leader并不会向Follow节点发送心跳包告诉其他Follow节点自己成为了Leader。
故障转移
当Sentinel选举出Leader之后,Leader就会开始执行故障转移,执行故障转移主要分为一下三步:
-
1、在已判定客观下线的master服务器的slave服务器中找到一个合格的slave服务器,向其发送
replicaof no one
命令,使其转换为master服务。 -
2、向其他从服务器发送
replicaof ip port
命令,使其成为新master服务的slave节点。 -
3、将已下线的master服务也设置为新的master服务的slave节点。
如何选举新的master节点
新的master选举条件主要需要参考4个因素:
-
1、断开连接时长:首先将所有于已下线master节点断开连接时间超过
down-after-milliseconds * 10
的slave节点删除掉,确保salve节点的数据都是比较新的。 -
2、slave节点的优先级排序:将所有的salve节点按照优先级进行排序,选出优先级最高的slave节点作为新的master节点(优先级由配置文件参数
replica-priority
决定,默认100)。 -
3、复制偏移量:如果有多个优先级相同的slave节点,则选出复制偏移量最大的的slave节点作为新的master节点。
-
4、进程id:如果还是没选出新的master节点