Redis 的复制功能分为同步(sync)和命令传播(command propagate)两个操作:
* 同步操作用于将从服务器的数据库状态更新到主服务器当前所处的数据库状态。
* 命令传播则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态。
接下来就对这两个操作进行介绍。
同步
当客户端向从服务器发送 SLAVEOF 命令,要求复制主服务器时,从服务器就首先需要执行同步操作,以使主从服务器的数据库状态首先保持一致。执行步骤如下:
1、从服务器向主服务器发送 SYNC 命令。
2、收到 SYNC 命令的主服务器执行 BGSAVE 命令,在后台生成一个 RDB 文件,并使用一个缓冲区记录同步期间执行的所有写命令。
3、主服务器将生成的 RDB 文件发送给从服务器,从服务器接收并载入该文件,以将自己的数据库状态更新至主服务器执行 BGSAVE 命令时的数据库状态。
4、主服务器将记录在缓冲区里的所有写命令发送给从服务器,从服务器将依次执行这些写命令。
命令传播
在同步操作完成后,主从服务器两者的数据库将达到一致状态,但这种一致并非一成不变,每当主服务器执行客户端发送的写命令时,主服务器的数据库状态就可能被修改。为了让主从服务器再次回到一致状态,主服务器就需要对从服务器执行命令传播操作,也即是将自己执行的造成主从服务器不一致的那条写命令发送给从服务器执行。
新版复制功能的实现
在 Redis 2.8 版本以前,如果从服务器在命令传播阶段与主服务器断开了连接,则当它重新连接到主服务器时,一旦发现两者的数据库状态不一致,就会重新进行一遍 SYNC 同步操作,从而大大影响了服务器性能。为了解决这种低效问题,Redis 2.8 版本后开始使用 PSYNC 命令代替 SYNC 命令来执行复制时的同步操作。
PSYNC 命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization)两种模式,其中完整重同步用于处理初次复制的情况,其执行步骤同 SYNC,而部分重同步则用于处理断线后重复制的情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将断线期间执行的写命令发送给从服务器,从服务器只需要执行这些写命令就可以将数据库更新至主服务器当前所处的状态。
部分重同步的实现
部分重同步功能由以下三个部分构成:
1、主、从服务器的复制偏移量(replication offset)。
2、主服务器的复制积压缓冲区(replication backlog)。
3、服务器的运行 ID。
一、复制偏移量
主服务器和从服务器会分别维护一个复制偏移量:主服务器每次向从服务器传播 N 个字节时,就将自己的复制偏移量的值加 N,而从服务器每次收到这 N 个字节时,也将自己的复制偏移量加 N。因此,通过比较主从服务器的复制偏移量,程序可以很容易地知道两者是否处于一致状态。
当一个从服务器在断线后重新连接到主服务器时,如果两者的复制偏移量不一样,从服务器就会向主服务器发送 PSYNC 命令,并告知以从服务器当前的复制偏移量。至于之后主服务器应该对从服务器执行完整重同步还是部分重同步,如果执行部分重同步,主服务器又该如何补偿从服务器断线期间丢失的那部分数据?这就和主服务器的复制积压缓冲区有关了。
二、复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度队列,默认大小为 1M(可以通过配置选项 repl-backlog-size 设置)。当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面。因此,主服务器的复制积压缓冲区里面会保存着最近一部分传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。
当从服务器重新连上主服务器时,从服务器会通过 PSYNC 命令将自己的复制偏移量offset 发送给主服务器,主服务器会根据这个偏移量来决定对从服务器执行何种同步操作:如果 offset 偏移量之后的的数据仍然在复制积压缓冲区中,则执行部分重同步(这时主服务器会先向从服务器发送 +CONTINUE 回复,表示数据同步将以部分重同步模式来进行);否则,执行完整重同步(这时主服务器会返回 +FULLRESYNC
三、服务器运行 ID
每个 Redis 服务器都会在启动时自动生成一个由 40 个随机十六进制字符组成的运行 ID。当从服务器初次复制主服务器时,它会先获取并保存主服务器的运行 ID,这样当它断开重新连上一个主服务器时,就会先把之前保存的运行 ID 发送给当前连接的主服务器:如果这个运行 ID 跟当前连接的主服务器的运行 ID 相同,说明从服务器断线前复制的就是这个主服务器,因此主服务器可以继续执行部分重同步操作;否则,主服务器将对该从服务器执行完整重同步操作。
心跳检测
在命令传播阶段,从服务器默认会以每秒一次的频率向主服务器发送“REPLCONF ACK
1、检测主从服务器的网络连接状态。如果主服务器超过一秒钟没有收到从服务器发来的 REPLCONF ACK 命令,则会认为两者之间的连接可能出现问题了。通过向主服务器发送“INFO replication”命令,在列出的从服务器列表的 lag 一栏中就可以看到相应从服务器最后一次向主服务器发送 REPLCONF ACK 命令距离现在过了多少秒。
2、辅助实现 min-slaves 选项。Redis 的 min-slaves-to-write 和 min-slaves-max-lag 两个选项可以防止主服务器在不安全的情况下执行写命令。在从服务器的数量少于 min-slaves-to-write 选项值,或者这些从服务器的延迟值(即上面的 lag 值)都不小于 min-slaves-max-lag 选项所设置的值时,主服务器将拒绝执行写命令。
3、检测命令丢失。如果写命令在传播过程中丢失,那么主服务器将从收到的 REPLCONF ACK 命令中发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会在复制积压缓冲区里找到从服务器缺少的数据,然后重新发送给从服务器。这个补发缺失数据的操作类似与部分重同步,不过它是在主从服务器没有断线的情况下执行的。
注:
参考书籍:《Redis 设计与实现》第 15 章——复制。