Skip to content

Redis之数据同步

生命太短,没留出时间给遗憾。若不是现在,那就永远不是。

Author:李东阳

1、sync同步

​ Redis2.8版本之前,首次通信成功后。slave会向master发送sync数据同步请求。然后master就会将其所有数据全部发送给slave,由slave保存到其本地持久化文件中。这个过程称为全量复制。

​ 但在这里存在一个问题,在全量复制过程中会存在网络抖动而导致数据复制过程中断。当网络恢复后,slave与master重新建立连接,此时slave会重新发送sync请求,然后重新进行全量复制。

​ 由于全量复制整个过程是非常消耗时间的,所以出现网络抖动的概率很高。而中断后从头开始是非常消耗资源和网络带宽的,所以这个问题需要解决。所以就有了psync同步。

2、psync同步

​ Redis版本之后,全量复制采用了psync同步策略。当全量复制过程中出现网络抖动而导致复制中断时,当重新连接成功后,复制过程可以进行”断点续传“。即从断开的位置进行复制,不用从头开始,大大提高了效率。

为了属性psync,整个系统做了三个变化:

1、复制偏移量

​ 系统为每个需要传输的数据进行了编号,该编号从0开始,每一个字节编一个号。该编号称为复制偏移量。参与复制的主从节点都会维护该复制偏移量。

2、主节点复制ID

​ 当master每次启动后就会自动动态生成一个长度为40为的16进制字符串作为当前master的复制ID。该ID是在进行数据同步时slave识别master使用的。通过info replication的master_replid属性可以查看到该ID。

3、复制积压缓冲区

​ 当master有连接的slave时,在master中就会创建并维护一个队列backlog,默认大小为1MB,该队列称为复制积压缓冲区。master接收到写操作数据不仅会写到master主存、master为每个slave发送数据的发送缓存,还会写到复制积压缓冲区。其作用就是用于保存最近操作的数据,以备”断点续传“时做数据补偿,防止数据的丢失。

3、psync同步过程

​ psync 是一个由 slave 提交的命令,其格式为 psync ,表示当前 slave 要从指定的 master 中的 repl_offset+1 处开始复制。repl_offset 表示当前 slave 已经完成复制的数据的 offset。该命令保证了“断点续传”的实现。

​ 在第一次开始复制时,slave 并不知道 master 的动态 ID,并且一定是从头开始复制,所以其提交的 psync 命令为 PSYNC ? -1。即 master_replid 为问号(?),repl_offset 为-1。

​ 如果复制过程中断后 slave 与 master 成功连接,则 slave 再次提交 psyn 命令。此时的 psyn命令的 repl_offset 参数为其前面已经完成复制的数据的偏移量。

​ 其实,并不是slave提交了psyn命令后就可以立即从master处开始复制,而是需要master给出响应结果后,根据响应结果来执行。master 根据 slave 提交的请求及 master 自身情况会给出不同的响应结果。响应结果有三种可能:

  • FULLRESYNC :告知 slave 当前 master 的动态 ID 及可以开始全量复制了,这里的 repl_offset 一般为 0

  • CONTINUE:告知 slave 可以按照你提交的 repl_offset 后面位置开始“续传”了

  • ERR:告知 slave,当前 master 的版本低于 Redis 2.8,不支持 psyn,你可以开始全量复制了

4、psync存在的问题

  • 在psync数据同步过程中,若slave重启,在slave内存中保存的master的动态ID与续传offset都会消失,”断点续传“将无法进行,从而只能进行全量复制,导致浪费资源。

  • 在psync数据同步过程中,master宕机后slave会发送“易主”,从而导致slave需要重新master进行全量复制,形成资源浪费。

5、psync同步的改进

Redis 4.0 对psync进行了改进,提出了“同源增量同步”策略。

5.1、解决slave重启问题

​ 针对“slave重启时master动态ID丢失问题”,改进后的psync将master的动态ID直接写入到了slave的持久化文件中。

​ slave重启后直接从本地持久化文件中读取master的动态ID,然后向master提交复制偏移量的请求,master会根据提交请求的slave地址,查找保存在master中的复制偏移量,然后向slave回复FULLRESYNC ,以告知 slave 其马上要开始发送的位置。然后 master 开始“断点续传”。

5.2、解决slave易主问题

​ slave易主后需要和新的master进行全量复制,其本质原因是新master不认识slave提交上来的psync请求中的“原master的动态ID”。如果slave发送

PSYNC <原 master_replid> 命令,新master能够识别出该slave要从原master复制数据,而自己的数据也都是从该master复制来的。那么新 master 就会明白,其与该 slave“师出同门”,应该接收其“断点续传”同步请求。

​ 而新 master 中恰好保存的有“原 master 的动态 ID”。由于改进后的 psync 中每个 slave都在本地保存了当前 master 的动态 ID,所以当 slave 晋升为新的 master 后,其本地仍保存有之前 master 的动态 ID。而这一点也恰恰为解决“slave 易主”问题提供了条件。通过 master的 info replicaton 中的 master_replid2 可查看到。如果尚未发生过易主,则该值为 40 个 0。

6、无盘操作

​ Redis 6.0 对同步过程又进行了改进,提出了“无盘全量同步”与“无盘加载”策略,避免了耗时的 IO 操作。

  • 无盘全量同步:master 的主进程 fork 出的子进程直接将内存中的数据发送给 slave,无需经过磁盘。

  • 无盘加载:slave 在接收到 master 发送来的数据后不需要将其写入到磁盘文件,而是直接写入到内存,这样 slave 就可快速完成数据恢复。

7、共享复制积压缓冲区

​ Redis 7.0 版本对复制积压缓冲区进行了改进,让各个 slave 的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有 slave的发送缓冲区,充分利用了复制积压缓冲区。