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 的动态 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
5.2、解决slave易主问题¶
slave易主后需要和新的master进行全量复制,其本质原因是新master不认识slave提交上来的psync请求中的“原master的动态ID”。如果slave发送
PSYNC <原 master_replid>
而新 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的发送缓冲区,充分利用了复制积压缓冲区。