Redis复制的实现

Redis 复制的实现 前面说了Redis复制的新旧复制以及新版复制的部分重同步的实现,部分重同步的实现主要依赖服务器运行ID、复制偏移量、复制积压缓冲区实现。本节具体介绍PSYNC命令实现、复制的实现以及心跳检测。

1 PSYNC命令的实现 Redis从2.8版本使用了PSYNC命令代替SYNC来执行复制时的同步操作。PSYNC命令调用有两种:

(1) 如果服务器之前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1命令,主动请求主服务器进行完整重同步(因为这时不可能执行部分重同步)。
(2) 如果从服务器之前复制过某个服务器,那么从服务器在开始一次新的复制时向主服务器发送PSYNC 命令:其中runid是上次复制的主服务器的运行ID,而offset则是从服务器的复制偏移量,接收到这个命令的主服务器根据这两个参数判断对从服务器执行哪种操作。
根据情况,接收到PSYNC命令的主服务器会向从服务器返回以下三种回复的一种:
(1) 返回+FULLRESYNC 回复,表示主服务器将于从服务器执行完整重同步操作:其中runid是这个主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量。
(2) 返回+CONTINUE回复,表示主服务器将与从服务器执行部分重同步操作,从服务器只需要执行自己缺失的命令即可。
(3) 返回-ERR回复,表示主从服务器版本低于Redis 2.8,它无法识别PSYNC命令,从服务器将向主服务器发送SYNC,并与主服务器执行完整同步操作。
Redis复制的实现
文章图片
PSYNC执行完整重同步和部分重同步时可能遇上的情况 2 复制的实现 2.1 设置主服务器的地址和端口
当客户端向服务器发送以下命令时:
127.0.0.1:12345> salveof 127.0.0.1 6379

服务器首先做的是将客户端给定的主服务器IP地址127.0.0.1以及端口6379保存到从服务器状态masterhost属性和masterport属性里面。
struct redisServer{ // ... // 主服务器的地址 char *masterhost; // 主服务器的端口 int masterport; // ... }

Redis复制的实现
文章图片
从服务器的服务器状态
SLAVEOF命令是一个异步命令,在完成masterhost属性和masterport属性设置工作之后,从服务器向发送SLAVEOF命令的客户端返回OK,表示复制指令已经被接收,而实际的复制工作在OK返回后才真正开始执行。
2.2建立套接字连接
在SLAVEOF命令执行之后,从服务器将根据命令所设置的IP地址和端口,创建连向主服务器的套接字连接。

Redis复制的实现
文章图片
从服务器创建连向主服务器的套接字
如果从服务器创建的套接字能成功连接(connect)到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的 文件事件处理器,这个处理器将负责执行后续的复制工作,比如接收RDB文件,以及接收主服务器传播来的写命令等。
Redis复制的实现
文章图片
主从服务器之间的关系
主服务器在接受(accept)从服务器的套接字连接之后,从服务器可以向主服务器发送命令请求,主服务将为该套接字创建相应的客户端状态,此时可以 将从服务器看作是主服务的客户端。
2.3 发送PING命令
从服务器成为主服务器的客户端之后,做的第一件事就是向主服务器发送一个PING命令。

Redis复制的实现
文章图片
从服务器向主服务器发送PING
这里的 PING命令的作用:
(1) 检测主从服务器之间的通信,判断套接字的读写状态是否正常。
(2) 由于复制工作需要主服务器正确处理命令,PING命令可以检测主服务器能否正常处理命令。
从服务器在发送PING命令后可能会遇到三种情况:

Redis复制的实现
文章图片
从服务器在发送PING命令时可能遇到的情况
2.4 身份验证
【Redis复制的实现】从服务器收到从服务器返回的PONG回复后,接下来就是决定是否进行身份验证:
如果从服务器设置了masterauth选项,那么进行身份验证,反之不进行身份验证。身份验证过程就是从服务器通过AUTH命令发送密码和主服务器requirepass选项所设置的密码进行比对,如果密码相同则通过验证继续向下执行,反之则不通过。不通过则中止目前的工作,重新创建套接字执行复制,直到身份验证通过,或者从服务器放弃执行复制为止。
2.5 发送端口信息
在身份验证步骤之后,从服务器将执行命令REPLCONF listening-port ,向主服务器发送从服务器的监听端口号。

Redis复制的实现
文章图片
从服务器向主服务器发送监听端口
这一步的作用就是在主服务器执行 INFO replication命令时打印出从服务器的端口号。
2.6 同步
从服务器将向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库更新至主服务器数据库当前所处的状态。并且在执行同步操作之后,主从服务器可以相互向对方发送命令请求,因此主从服务器双方都是对方的客户端。
2.7 命令传播
同步完成后,主从服务器进入命令传播阶段,这时主服务器只要将自己执行的写命令发送给服务,而从服务器只要一直接受来自主服务器的写命令,将自己的数据库状态和主服务器的数据库的状态保持一致。
3 心跳检测 在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK ,其中replication_offset是从服务器当前的复制偏移量。
心跳检测的作用:
(1) 检测主从服务器的网络连接状态。
(2) 辅助实现min-slaves选项。
(3) 检测命令丢失 。
3.1 检测主从服务器的网络状态
主从服务器可以通过发送和接收REPLCONF ACK命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没有收到从服务器发来的REPLCONF ACK命令, 那么主服 务器就知道主从服务器之间的连接出现问题了。
在一般情况下, 延迟回复的值应该在0秒或者1秒之间跳动, 如果超过1秒的话, 那么说明主从服务器之间的连接出现了故障。
3.2 辅助实现min-salves配置选项
Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。如主服务器配置:
min-slaves-to-write 3
min-slaves-max-lag 10
那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10 秒时, 主服务器将拒绝执行写命令。
3.3 检测命令丢失
如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失, 那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。
例如,主从服务器某一时刻处于一致状态,由于网络故障导致传播途中丢失了命令,导致主从服务器状态不一致。

Redis复制的实现
文章图片
主从服务器处于一致状态
Redis复制的实现
文章图片
主从服务器状态不一致
在此之后,当从服务器向主服务发送 REPLCONF ACKE命令时,主服务器发现从服务器的复制偏移量仍为200,这说明从服务器丢失了数据,所以主服务会从复制积压缓冲区中将从服务器丢失的33个字节的命令发送给从服务器,从服务器接收后执行可以将自己更新至主服务器当前所处的状态。
Redis复制的实现
文章图片
主服务器向从服务器补发缺失的数据
主服务器向从服务器补发缺失数据这一操作的原理和部分重同步操作的原理非常相似,这两个操作的区别在于,补发缺失数据操作在主从服务器没有断线的情况下执行,而部分重同步操作则在主从服务器断线并重连之后执行。
由于复制积压缓冲区是Redis 2.8版本新增,所以之前即使有命令在传播过程中丢失,主从服务器都不会注意到。
4 小结 (1) 主服务在接收到从服务器的PSYNC命令会根据从服务器发送的runid和offset两个参数,对从服务器执行不同的操作。
(2) 主从服务器通过向从服务器传播命令来更新从服务器的状态,保持主从服务器一致,而从服务则通过向主服务器发送命令来进行心跳检测,已经命令丢失检测。
本文完
注:本文参考《Redis设计与实现》,如发现错误,请指正!

    推荐阅读