1. 前言
和MySQL主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis主从复制可以根据是否是全量分为全量同步和增量同步。
2. 旧版复制功能实现
redis复制功能分为同步和命令传播两种操作:
(1)同步操作负责将从数据库的状态更新为和主数据库状态一致
(2)命令传播操作则用于当主服务器状态修改时,让从服务器状态重新回到一致
2.1 同步
同步操作由sync操作完成:
(1)从服务器向主服务器发送sync命令
(2)收到sync命令的主服务器执行BGSAVE命令,生成RDB文件,并使用一个缓冲区缓存生成RDB期间所有的写命令
(3)RDB生成完成时,发送RDB给从服务器,从服务器载入RDB,状态和主服务器一致
(4)然后主服务器将缓冲区中的写命令全部传给从服务器,状态一致
2.2 命令传播
同步操作完成后,主从状态一致,但是每当客户端向主服务器执行写命令时,主服务器会修改,和从服务器状态不一致,为了让主从状态重新回到一致,主服务器会发送写命令给从服务器,让从服务器回到和主服务器一致的状态。
2.3 旧版复制功能的不足之处
在redis2.8之前,主从服务器的复制会分为两种:
(1)初次复制:从服务器以前从没复制过其他主服务器,或者从服务器的主服务器换了
(2)断线后复制:处于命令传播阶段的主从服务器,发生了断线,重新连上后,从服务器继续复制主服务器
为什么说效率低呢?因为在旧版复制中,断线重新连接后进行的复制,是通过sync命令实现的,而之前说了sync命令实现是主服务器重新生成RDB文件,发送给从服务器,重新生成RDB是很低效的,而且主服务器生成RDB时是阻塞状态的,所以旧版复制是低效率的。
3. 新版复制功能实现
为了解决旧版复制低效问题,新版复制采用了psync命令来代替sync命令,psync命令具有完全重同步和部分重同步:
(1)完全重同步:和上面的sync一样
(2)部分重同步:专门用于负责处理断线后重连,通过重连后只执行断线期间的写命令而不是全同步来达到高效
3.1 部分重同步实现
部分重同步实现由以下三部分构成:
(1)主服务器的复制偏移量和从服务器的复制偏移量
(2)主服务器的复制积压缓冲区
(3)服务器运行ID
3.1.1 复制偏移量
主从服务器都会维持一个复制偏移量:
- 主服务器每次向从服务器发送n个字节的命令,就在自己复制偏移量加n
- 从服务器每次接收到n个字节数据,就在自己复制偏移量加n
例如,主从服务器当前偏移是100,主向从服务器发送33个字节命令,那么主从此时的复制偏移量都是133,所以判断主从当前状态是否一致,可以通过复制偏移量来判断:复制偏移量如果一样,就代表状态一致,否则不一致。如果断线时,从服务器因为收不到命令,而主服务器一直发送命令,这时的偏移量肯定不一样,所以状态不一致,那么如果让中间漏掉的命令重新传给从服务器呢,这和积压缓冲区有关。
3.1.2 复制积压缓冲区
复制积压缓冲区是一个主服务器维护的固定长度的先进先出队列。每当主服务器进行命令传播时,它不仅将命令传给从服务器,还会将命令写入到缓冲区:
如上图所示,就是一个积压缓冲区的现状,当从服务器重连后,向主服务器发送一个psycn命令,同时会将从服务器当前的复制偏移量(offset)带过去,主服务器会根据从服务器的offset,来决定是全同步还是部分同步:
(1)如果从服务器的offset是10086,那么此时要复制的偏移量应该是从10087开始,在缓冲区存在,将从10087开始往后所有数据都发送给从服务器,所以执行的是部分重同步
(2)相反的,如果不存在,就需要进行全同步了!
如下图:如果进行部分重同步,就向从服务器发送一个CONTINUE回复。
3.1.3 服务器运行ID
除了复制偏移量和复制积压缓冲区之外,实现部分重同步还需要用到服务器运行ID(run ID):
- 每个Redis服务器,不论主服务器还是从服务,都会有自己的运行ID;
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来(注意哦,是从服务器保存了主服务器的ID)。
当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID:
- 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作;
- 相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。
4. 心跳机制
在命令传播阶段时,从服务器每隔一秒都会向主服务器发送命令:REPLCONF ACK <replication_offset>,每发送一次这个命令从服务器都会向主服务器报告一次自己的复制偏移量,如果超过一秒,就代表主从连接出了问题。那此时尽管主服务器发送给从服务器的SET key value丢失了。也无所谓,主服务器马上就知道了,但是要注意,这时的偏移量不一致,并不是断线导致的,而是以为主服务器传给从服务器的命令丢失了,连接情况还是正常的,这种称之为命令丢失。
来源:https://www.cnblogs.com/Booker808-java/p/9866155.html