接上篇,《redis的主从同步(一)2.8版本以前的方式》,在最后,讲了到了2.8版本之前的方式有缺陷,大家还记得吧,今天来讲讲从2.8版本开始redis主从同步方式的改进
先来回顾下2.8版本之前redis主从同步方式的缺陷
- 如果同步(sync)操作完成后,同步了已有的数据,此时,主从服务器之间处于命令传播 (command propagate) 阶段,此时会传递后续的命令,然后主从服务器之间断开连接
- 当主从服务器重新建立连接后,主从服务器之间又开始执行同步(sync)操作
- 此时的同步(sync)操作,主服务器生成RDB文件,RDB文件包含的key是从第一次同步中的第一个命令开发开始
- 虽然还是可以完成同步(sync)操作,但是其实有很多key是没有必要再次同步的
- 因为同步(sync)操作中,需要主服务器生成RDB文件,这个操作会占用服务器的大量的资源:CPU、内存、IO
- 而主从服务器之间传递RDB文件会占用服务器带宽
- 从服务器加载RDB文件时,同样会占用服务器的大量的资源:CPU、内存、IO
- 所以没有没有必要的同步(sync)操作会损耗服务器资源,降低服务的性能
总结下来就是,老版的主从同步方式对于断线重连情况下很低率,所以2.8版本开始通过PSYNC
命令代替了SYNC
命令来执行同步操作
PSYNC命令
PSYNC
命令有两种模式:完整同步模式(full resynchronization)
和部分同步模式(partial resynchronization)
两种模式
完整同步模式(full resynchronization)
- 完整同步模式用于处理
初次复制
情况
- 完整同步模式的执行过程和
SYNC
命令一致,我们顺便回顾下,过程如下:
![同步(sync)操作的流程](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9e2774ebcbe44e7ea64baed7419b34db~tplv-k3u1fbpfcp-watermark.image)
- 1.从服务器向主服务器发送slaveof命令
- 2.主服务器接受到slaveof命令后
- 3.开始执行BGSAVE命令
- 4.在后台生成一个RDB文件
- 5.并使用一个缓冲区记录从开始执行的所有命令
- 6.主服务器BGSAVE命令执行完毕
- 7.主服务器将BGSAVE命令生成的RDB文件发送至从服务器
- 8.从服务器接受RDB文件并且载入
- 9.将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
- 10.主服务器将记录在缓冲区中里所有的写命令发送给从服务器
- 11.从服务器执行这些命令完成整个同步过程
部分同步模式(partial resynchronization)
- 部分同步模式主要来处理
断线重连后的复制场景
- 别的和
SYNC
命令一致,只有在断线重连后,主节点会把断线断线期间的命令同步过从节点,不会在重新同步所有的命令了
来看下断线重连情况下的PSYNC
命令执行过程
时间 |
主服务器 |
从服务器 |
T0 |
主从服务器完成同步 |
主从服务器完成同步 |
T1 |
执行传播命令,传播 set k1 v1 命令 |
执行主服务器传播过来的 set k1 v1 命令 |
T2 |
执行传播命令,传播 set k2 v2 命令 |
执行主服务器传播过来的 set k2 v2 命令 |
T3 |
此时发生故障,主从服务器断开链接 |
此时发生故障,主从服务器断开链接 |
T4 |
set k3 v3 |
此时已经和主服务器断开连接,set k3 v3 没有传播过来 |
T5 |
set k4 v4 |
此时已经和主服务器断开连接,set k4 v4 没有传播过来 |
T6 |
此时故障恢复,主从服务器重新链接 |
此时故障恢复,主从服务器重新链接 |
T7 |
|
向主服务器发送 PSYNC命令 |
T8 |
向从服务器发送确认回复,执行部分同步 |
接受到主服务器的确认回复,执行部分同步 |
T9 |
向从服务器发送set k3 v3、set k4 v4两个命令 |
|
T10 |
|
接收到主服务器发送过来的set k3 v3、set k4 v4两个命令,并执行 |
T11 |
此时主从服务器完成部分同步 |
此时主从服务器完成部分同步 |
主从服务器的复制偏移量
- 主从服务双方都会维护一个复制偏移量
- 主服务器每次给从服务器传播n个字节,就将自己维护的偏移量加n
- 从服务每次接收到主服务器传播过来的数据时,就将自己维护的偏移量加上数据大小的字节数
- 主从偏移量栗子:
主服务器偏移量:offset=100
从服务器A偏移量:offset=100
从服务器B偏移量:offset=100
复制代码
- 此时说明主服务和从服务器A、从服务器B都处于同步一致的状态
- 当主服务向从服务同步100字节数据后
主服务器偏移量:offset=100
从服务器A偏移量:offset=200
(从服务器B和主服务器断开链接)从服务器B偏移量:offset=100
复制代码
- 此时说明主服务和从服务器A处于同步一致的状态
- 而从服务B因为断线原因导致从服务器B的偏移量 offset=100,可以判断出来他们不处于数据同步状态
主服务器的复制积压缓存区
复制积压缓存区
是由主服务器维护的一个队列,默认大小为1MB
- 该队列是
固定长度
和先进先出
的
- 当主服务器对从服务器执行
命令传播
时,不光会发送所有命令给所有从服务
- 还会把所有命令写入
复制积压缓存区
中,如图:
![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d529992d6f614da58e0daa25c4dd6c55~tplv-k3u1fbpfcp-watermark.image?)
- 因为
复制积压缓存区
是队列
,是把主服务的每一个命令的字节写入到队列中,包含两部分,偏移量
和命令字节值
- 队列结构如下:
Node1:
offset=1
val=s
Node2:
offset=2
val=e
Node3:
offset=3
val=t
Node4:
offset=4
val=k
Node5:
offset=5
val=1
Node6:
offset=6
val=v
Node7:
offset=7
val=1
复制代码
- 看到队列结构,就是把主服务的命令
set k1 v1
的每一个字节和字节对应的偏移量
存放到复制积压缓存区
中
- 当从服务器重连上主服务器上时,从服务器会通过
PSYNC
将自己的复制偏移量offset
发送给主服务器,主服务器会根据这个复制偏移量来判断是使用完整同步
还是 部分同步
,具体操作如下:
时间 |
主服务器 |
从服务器 |
T0 |
此时发生故障,主从服务器断开链接 |
此时发生故障,主从服务器断开链接 |
T1 |
此时故障恢复,主从服务器重新链接 |
此时故障恢复,主从服务器重新链接 |
T2 |
|
向服务器发送 PSYNC命令,命令中会将自己的复制偏移量发送过去 |
T3 |
接受到从服务器传过来的复制偏移量进行判断 |
|
T4 |
如果该复制偏移量以后的数据还存在复制缓存区里面,就执行部分同步,数据从复制缓存区取 |
|
T5 |
若果该复制偏移量以后的数据不存在了(因为队列先进先出),所以要执行完整同步 |
|
redis服务的运行id
- 每一个redis服务启动时候,都会有自己的运行id
- 每个id都不会重复,具体怎么实现的不看了
- 当从服务和主服务器进行初次复制时,从服务会保存主服务的运行id
- 在断线重连后,执行
PSYNC
命令是,会比较该id,如果前后id一致,主服务在通过判断复制偏移量
判断是需要完整同步还是部分同步,如果前后id不一致,那么说明前后的主服务不一样,所以选用完整同步
PSYNC
命令执行的详细过程
时间 |
主服务器 |
从服务器 |
T0 |
主服务器启动,生成唯一运行id(假装模拟一个)=1 |
从服务器启动,生成唯一运行id(假装模拟一个)=2 |
T1 |
此时,主从初次复制完成(假设没有命令),主偏移量offset=0,复制积压缓存区中间没有数据 |
此时,主从初次复制完成(假设没有命令),从偏移量offset=0 |
T2 |
执行传播命令,传播 set k1 v1 命令,把该命令写入复制积压缓存区,主偏移量offset=9 |
执行主服务器传播过来的 set k1 v1 命令,从偏移量offset=9 |
T3 |
执行传播命令,传播 set k2 v2 命令,把该命令写入复制积压缓存区,主偏移量offset=18 |
执行主服务器传播过来的 set k2 v2 命令,从偏移量offset=18 |
T4 |
此时发生故障,主从服务器断开链接 |
此时发生故障,主从服务器断开链接 |
T5 |
执行set k3 v3命令,把该命令写入复制积压缓存区,主偏移量offset=27 |
此时已经和主服务器断开连接,set k3 v3 没有传播过来 |
T6 |
执行set k4 v4命令,把该命令写入复制积压缓存区,主偏移量offset=36 |
此时已经和主服务器断开连接,set k4 v4 没有传播过来 |
T7 |
此时故障恢复,主从服务器重新链接 |
此时故障恢复,主从服务器重新链接 |
T8 |
|
向主服务器发送 PSYNC命令,带了上次同步的主服务运行id=1,从服务器的偏移量offset=18 |
T9 |
主服务器接受到PSYNC命令,通过对于运行id,发现是断线之前的两个服务器 |
|
T10 |
主服务器在判断从服务器发送过来从服务偏移量,从服务器发送过来的偏移量offset=18,主服务判断复制积压缓存区中offset=18以后的数据是否存在,发现数据存在,主服务器判断使用部分同步 |
|
T11 |
向从服务器发送确认回复,执行部分同步 |
接受到主服务器的确认回复,执行部分同步 |
T12 |
主服务从复制积压缓存区中偏移量offset=18以后的舒服,向从服务器发送set k3 v3、set k4 v4两个命令 |
|
T13 |
|
接收到主服务器发送过来的set k3 v3、set k4 v4两个命令,并执行,并更新从服务器的偏移量offset=36 |
T14 |
此时主从服务器完成部分同步,他们的偏移量都为offset=36 |
此时主从服务器完成部分同步,他们的偏移量都为offset=36 |
今天讲了redis的主从同步中的2.8版本以后的方式,欢迎大家来交流,指出文中一些说错的地方,让我加深认识,愿大家没有bug,谢谢!