还记得上面说的内容吗 —— 2.8 版本开始,进行主从同步可能只需要执行命令传播即可。这个也是因为 sync 比较耗资源,从而采取的优化。
那什么时候可以这么做呢?我们先看下前提条件:
主从同步实际分 2 种情况:
初次复制:从服务器第一次复制当前主服务器(PS:主服务器是有可能更换的)
断线后重复制:处于命令传播阶段的主从服务器,因为网络问题而中断复制,从服务器通过自动重连,重新连接上主服务器并继续复制。
在断线后重复制的情况下,在 2.8 版本之前,会再次执行同步(sync 命令)和命令传播。
如果说,在断线期间,主服务器(已有上万键值对)只执行了几个写命令,为了让从服务器弥补这几个命令,却要重新执行 sync 来生成新的 rdb 文件,这也是非常低效的。
为了解决这个问题,2.8 开始就使用 psync 命令来代替 sync 命令去执行同步操作。
psync 具有完整重同步和部分重同步两种模式:
完整重同步:用于初次复制情况,执行过程同 sync,在这不赘述了。
部分重同步:用于断线后重复制情况,如果满足一定条件,主服务器只需要将断线期间执行的写命令发送给从服务器即可。
因此很明显,当主从同步出现断线后重复制的情况,psync 的部分重同步模式可以解决 sync 的低效情况。
上面的介绍中,出现了「满足一定条件」,那又是鬼什么条件呢?—— 其实就是一个偏移量的比较,具体可以继续往下看。
2.4 部分重同步的实现
部分重同步功能由以下 3 部分组成:
主从服务器的复制偏移量
主服务器的复制积压缓冲区
服务器的运行 id(run id)
2.4.1 复制偏移量
执行复制的主从服务器都会分别维护各自的复制偏移量:
主服务器每次向从服务器传播 n 个字节数据时,都会将自己的复制偏移量加 n。
从服务器接受主服务器传来的数据时,也会将自己的复制偏移量加 n
举个例子:
若当前主服务器的复制偏移量为 10000,此时向从服务器传播 30 个字节数据,结束后复制偏移量为 10030。
这时,从服务器还没接收这 30 个字节数据就断线了,然后重新连接上之后,该从服务器的复制偏移量依旧为 10000,说明主从数据不一致,此时会向主服务器发送 psync 命令。
那么主服务器应该对从服务器执行完整重同步还是部分重同步呢?如果执行部分重同步的话,主服务器又如何知道同步哪些数据给从服务器呢?
以下答案都和复制积压缓冲区有关
2.4.2 复制积压缓冲区
首先,复制积压缓冲区是一个固定长度,先进先出的队列,默认 1MB。
当主服务器进行命令传播时,不仅会将命令发送给从服务器,还会发送给这个缓冲区。
因此复制积压缓冲区的构造是这样的:
当从服务器向主服务器发送 psync 命令时,还需要将自己的复制偏移量带上,主服务器就可以通过这个复制偏移量和复制积压缓冲区的偏移量进行对比。
若复制积压缓冲区存在从服务器的复制偏移量 + 1 后的数据,则进行部分重同步,否则进行完整重同步。
2.4.3 run id
运行 id 是在进行初次复制时,主服务器将会将自己的运行 id 发送给从服务器,让其保存起来。
当从服务器断线重连后,从服务器会将这个运行 id 发送给刚连接上的主服务器。
若当前服务器的运行 id 与之相同,说明从服务器断线前复制的服务器就是当前服务器,主服务器可以尝试执行部分同步;
若不同则说明从服务器断线前复制的服务器不是当前服务器,主服务器直接执行完整重同步。
花了很多笔墨,终于把部分重同步的实现写完了,最后补充一个辅助功能
2.5 心跳检测
刚才提到,主从同步有同步和命令传播 2 个步骤。