主从机连接过程
redis通过搭建主从机,主机负责写操作,并将数据同步给从机,然后在和从机共同承担读操作可以大大提高redis的响应速度。
当我们在从机配置文件中配置replicaof(老版本是salveof)加上主机ip时,从机启动后则会主动向这个ip请求建立tcp连接。成功建立tcp连接后,从机会像主机发送一条指令psync ? -1(具体意思后面说),接收到这条指令后,主机返回+FULLRESYNC
全量复制是主机生成rdb文件,并将rdb文件发送给从机,从机在根据rdb文件恢复和主机一样的数据。 rdb文件是redis服务器的一次快照,保存当前服务器的全部数据,具体可以看我的另一篇文章:redis持久化存储,rdb快照文件,aof文件
https://blog.csdn.net/dxh9231028/article/details/140842100?spm=1001.2014.3001.5501 ;
主机在创建要发送给从机的rdb文件的时刻之后,所有的写操作命令都会存入复制缓冲区,并且通过tcp连接将写操作命令传给从机,从机执行这个指令来保证和主机数据同步。整个过程如下图所示
从机故障恢复
在连接期间主机会周期性向从机发送ping指令,从机回复pong指令已确认从机存活,当既定时间没有收到从机回复的pong时则标记从机为断连,释放TCP连接,从机恢复后会重新尝试和主机连接。
在redis2.8版本之前,主从机的复制请求是通过sync完成的,sync无论从机是否保存部分主机数据,都会让主机进行全量复制,但假如从机只是短暂的暂停一下,并没有和主机数据相差太多呢?此时进行全量复制则是性能的极大浪费(全量复制复制全部redis的数据及其耗时),而2.8版本之后则出现了psync指令,主机在向复制缓冲区插入写操作指令时会维护一个offset,offset会记录主机向复制缓冲区插入的多少字节的指令,而从机也会维护一个offset,记录接受了多少字节的指令,从机在进行持久化存储时也会将offset存入rdb文件或aof文件,这样在从机短暂宕机重启后会向主机发送psync
增量复制就是从复制缓冲区中,从机保存的宕机之前的offset开始复制指令,传输给从机进行数据同步。
但复制缓冲区的空间是有限的(默认是1mb)。当从机宕机时间过长,从机保存的offset和主机保存的offset的差值已经大于1mb,那么复制缓冲区内的指令则不足以让从机和主机数据同步,这时则必须执行全量复制。
比如说从机的offset保存的是1mb偏移量,而主机保存的是2.5mb偏移量,由于缓冲区只能保存1mb数据,所以此时复制缓冲区只有1.5mb偏移量到2.5mb偏移量的数据,1mb偏移量到1.5偏移量的数据则无法通过增量恢复来同步数据。
如果从机宕机期间主机也宕机了呢?或者由于某些原因,这个ip和端口已经换了一个全新的redis服务了呢?这将导致虽然从机保存的offset和主机保存的offset的差值可能小于1mb,但此时主机的复制缓冲区内部保存的指令已经不是之前的指令了。
这时replid的作用就凸显出来了,replid是主机创建时生成的一个独一无二的复制id,在第一次建立连接时主机就通过+FULLRESYNC
了解了offset和replid后,我们便可以解释第一段的指令,首先从机第一次完成TCP连接时,并不知道replid和offset,所以用?代替replid,-1代替offset,代表请求全量复制,也就是psync ? -1指令。而主机在第一次全量复制时也通过+FULLRESYNC
哨兵原理
redis集群(了解redis和哨兵集群如何搭建可以看我的另一篇文章redis集群搭建,哨兵集群搭建)中为了防止主机宕机导致集群瘫痪,引入了哨兵集群,哨兵集群通过实时监控主机是否正常运行,并且在主机出现故障时及时将另一个满足条件的从机变为新的主机以保证redis集群正常运转,那这一过程具体发生了什么呢?
首先,哨兵集群是如何对redis集群的主机实现监控的呢?
哨兵集群的每个节点会固定周期的向主机发送ping指令,等待主机响应,一旦主机超过既定时间未响应(哨兵配置文件中的sentinel down-after-milliseconds配置的值),则该哨兵节点则认定该主机为主观下线(主观下线的意思是,当前节点主观认为该主机故障无法响应,但仍然不能确定是否真的是主机故障,也有可能是当前哨兵节点所处网络环境不好,或者其他原因),当认为主机为主观下线的哨兵节点数量达到指定数量时(哨兵配置文件中配置的sentinel parallel-syncs,通常设置为所有哨兵节点数量的一半以上),则认为该主机为客观下线(客观下限是达到配置文件中既定数量的哨兵节点认为该主机为下线状态,则判定主机确实是出故障了),这时候就会开始选举新的主机。
在挑选新的主机之前,首先哨兵集群要先选举一个老大来负责发送命令选举新的主机,选举过程中,哨兵集群会进行投票,哨兵节点会观察哪个节点的票最多,如果有一个节点的票最多,则会投票给该节点,那第一个投票的哨兵节点怎么办呢?如果当前投票哨兵节点为第一个,那么就会发现没有哨兵节点得到其他哨兵节点的投票,这时他就会投票给自己,当他自己有了第一票后,他就是最多得票的哨兵节点,那么后续所有的哨兵节点都会投票给他,换句话说,谁先投票,谁就会被选举为哨兵集群的老大,而最先发起投票的通常是最先发现主机客观下线的,这样来说,假设我们配置n个哨兵节点发现redis主节点主观下限时认为该节点客观下线,那么第n个发现主机主观下线的哨兵节点就会是第一个发现主机客观下线的哨兵节点,他会当选哨兵集群老大。
选举完哨兵集群老大后,该节点会继续根据一些规则选举新的主机
- 首先判断主机与从机断开时间长短,如果超过指定值( down-after-milliseconds * 10 )则会 排除该从机
- 然后在判断从机配置中的 slave-priority 值,越小优先级越高,如果是 0 则永不参与选举(通常这个值是相等的)
- 如果 slave-prority 一样,则判断 slave 节点的 offset 值,越大说明数据越新,优先级越高
- 如果依然相等,则是判断 从机的 id 大小,越小优先级越高。
其实这些条件都在保证从机的数据尽可能的新,最后通过id是确保能够选出来一个作为主机(并不是id越小,数据越新)
当确认新主机后,哨兵集群老大会首先向新主机发送 slaveof no one 命令让其脱离从机身份变成主机,然后在向其他从机发送 slaveof 新主机ip 命令让其他从机跟随新的主机同数据,最后将之前的主机ip标记为从机,当旧主机正常运行时则会变为新主机的从机。
这样一个主机发生故障,哨兵集群发挥作用的流程就结束了。