1. 相关概念

1.1 全量复制
用于初次复制或其它无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个IE非常重型的操作,当数据量较大时,会对主从节点和网络造成很大的开销
1.2 部分复制
用于处理主从复制中因网络闪断原因造成的数据丢失场景,当从主节点再次连接上主节点后,如果(条件允许),主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销,需要注意的是,如果网络中断时间过长,造成主节点没有能够完整的保存中断期间执行的写命令,则无法进行部分复制,仍使用全量复制
1.3 复制偏移量
参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息会在info replication 中的master_repl_offset指标中
image.png

从节点(slave)每秒钟上报自身的复制偏移量给主节点,因为主节点也会保存从节点的复制偏移量,统计指标如下
【Redis】全量复制和部分复制 - 图2

从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info replication中的slave_repl_offset
1.4 复制积压缓冲区
复制积压缓冲去是保存在主节点上的一个固定长度的队列,默认大小为1mb,当主节点有连接从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区。
【Redis】全量复制和部分复制 - 图3

在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset) 。由于复制积压缓冲区定长且先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

2. Redis全量复制过程

【Redis】全量复制和部分复制 - 图4

  1. Redis 内部会发出一个同步命令,刚开始是 Psync 命令,Psync ? -1表示要求 master 主机同步数据
  2. 主机会向从机发送 runid (redis-cli info server)和 offset,因为 slave 并没有对应的 offset,所以是全量复制
  3. 从机slave会保存主机master的基本信息save masterinf
  4. 主节点收到全量复制的命令后,执行bgsave(异步执行),在后台生成RDB文件(快照),并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有命令
  5. 主机send RDB发送RDB文件给从机
  6. 发送缓冲区数据
  7. 刷新旧的数据,从节点在载入主节点的数据之前要先将老数据清除
  8. 加载RDB文件将数据库状态更新至主节点执行bgsave时的数据库状态和缓冲区数据加载

全量复制开销的方面
bgsave时间
RDB文件网络传输时间
从节点清空数据的时间
从节点加载RDB的时间

3. 部分复制

部分复制是 Redis 2.8 以后出现的,之所以要加入部分复制,是因为全量复制会产生很多问题,比如像上面的时间开销大、无法隔离等问题, Redis 希望能够在 master 出现抖动(相当于断开连接)的时候,可以有一些机制将复制的损失降低到最低
【Redis】全量复制和部分复制 - 图5

1. 如果网络抖动(连接断开 connection lost)
2. 主机master 还是会写 replbackbuffer(复制缓冲区)
3. 从机slave 会继续尝试连接主机
4. 从机slave 会把自己当前 runid 和偏移量传输给主机 master,并且执行 pysnc 命令同步
5. 如果 master 发现你的偏移量是在缓冲区的范围内,就会返回 continue 命令
6. 同步了 offset 的部分数据,所以部分复制的基础就是偏移量 offset。

4. 正常情况下Redis是如何决定全量复制还是部分复制(增量复制)

从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制
如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤压),则执行全量复制
缓冲区大小调节:
由于缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲去的小打(通过配置repl_backlog_size)来设置;例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100kb,则复制积压缓冲区的平均需求为6MB,保险起见可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。
服务器运行ID(runid)
每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符串组成,runid用来唯一识别一个Redis节点。通过info server命令,可以查看runid

【Redis】全量复制和部分复制 - 图6

主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来,当断线重连时,从节点会将这个runid发送给主节点,主节点根据runid判断能否进行部分复制;
如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)
如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制