复制

在Redis中,用户可以通过SLAVEOF命令或者在配置文件中设置slaveof选项,让一个服务器去复制另一个服务器,被复制的服务器称为主服务器,而执行复制任务的服务器称为从服务器;进行复制中的主从服务器将保存相同的数据,这种现象称为数据库状态一致;

1. 旧版复制功能的实现

Redis复制功能分为同步和命令传播两个操作:

同步是让从服务的数据库状态更新至主服务器的数据库状态;

命令传播则是指当主服务器的数据库状态发生变化,导致主从服务器数据库状态不一致时,让主从服务器的数据库重新回到一致状态;

1.1 同步

从服务器通过向主服务器发送SYNC命令,来完成同步操作,同步操作的步骤包括;

1). 从服务器向主服务器发送SYNC命令;

2). 主服务器收到SYNC命令后,则会执行BGSAVE命令,生成RDB文件,同时使用一个缓冲区记录从现在开始执行的所有写命令;

3). 主服务器将生成的RDB文件发送给从服务器,从服务器载入该RDB文件,将数据库状态更新至生成RDB文件时主服务器的数据库状态;

4). 主服务器将缓冲区中的写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器当前所处的状态;

1.2 命令传播

当主服务器执行客户端发送的写命令时,会导致主从服务器数据库的状态再次不一致,此时主服务器会将执行的写命令发送给从服务器,从服务器执行该命令,将主从服务器的数据库状态再次更新为一致状态;

2. 旧版复制功能的缺陷

从服务器的复制操作分为两种情况:

1). 初次复制:从服务器之前从未执行过复制操作, 或者从服务器当前复制的主服务器和之前复制的主服务器不一致;

2). 断线后重连:处于命令传播阶段主从服务器因为网络原因发生中断,之后从服务器重新连接上主服务器,并且继续复制主服务器;

旧版复制功能可以很好的完成初次复制的任务,但是对于断线后重连,旧版复制功能需要再次执行SYNC命令,并且向从服务器发送大量重复的键值对数据,因此效率十分低下;

SYNC命令是一个十分耗费资源的操作:

1). 主服务器执行BGSAVE命令生成RDB文件时,生成操作会占用主服务器大量的CPU、内存以及磁盘I/O资源;

2). 主服务器将生成的RDB文件发送给从服务器时,会占用大量的网络资源,同时也会影响主服务器对于客户端发送的请求做出及时响应;

3). 从服务器载入RDB文件时,主进程会一直处于阻塞状态,影响从服务器处理命令请求;

3. 新版复制功能的实现

从Redis2.8版本开始,使用PSYNC命令来代替SYNC命令执行复制操作,PSYNC命令具有完整重同步和部分重同步两种模式;

完整重同步用于处理初次复制的情况,主服务器执行BGSAVE命令生成RDB文件,并且使用缓冲区记录执行的写命令,然后将这些数据发送给从服务器,从服务器进行载入操作,即可将主从服务器的数据库更新为一致状态;

部分重同步用于断线后重新复制的情况,当从服务器重新连接上主服务器之后,在条件允许的情况下,主服务器会将断线期间执行的所有写命令发送给从服务器,从服务器接收并执行这些写命令,即可将主从服务器的数据库状态更新为一致;

4. 部分重同步功能的实现

部分重同步功能的实现依赖于三部分:主从服务器的复制偏移量、主服务器的复制积压缓冲区、服务器的运行ID;

复制偏移量:主服务器向从服务器发送N字节的数据,则自己的复制偏移量会加上N;从服务器接收主服务器发送的N字节数据时,也会将自己的复制偏移量加上N;

通过对比主从服务器的复制偏移量,程序可以轻易辨别主从服务器的数据库状态是否一致;

复制积压缓冲区:复制积压缓冲区是一个固定长度的先进先出的队列,默认大小为1MB;当入队元素的数量大于队列长度时,会将队首元素弹出,然后将新元素加入队列;

当主服务器进行命令传播时,不仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面,并且会为复制积压缓冲区中的每个字节记录相应的复制偏移量;

当从服务器断线重连后,会通过PSYNC命令将自己的复制偏移量发送给主服务器,主服务器会根据这个复制偏移量来决定执行何种操作:
1). 当可以在复制积压缓冲区中找到从服务器的复制偏移量时,会将偏移量后的所有数据发送给从服务器,执行部分重同步操作;

2). 当不能在复制积压缓冲区中找到从服务器的复制偏移量时,主服务会执行完整重同步操作;

复制积压缓冲区大小 = 2 * second * write_size_per_second;

服务器运行ID:每个Redis服务器,无论主从,都会有自己的运行ID;服务器运行ID由40个随机的十六进制字符组成;当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID发送给从服务器,从服务器会将这个运行ID保存起来;当断线重连后,会通过PSYNC命令将记录的runID同步发送给主服务器,主服务器会根据这个ID判断从服务器之前复制的对象是否是自己,如果是自己,则尝试进行部分重同步操作,否则,直接进行完整重同步操作;

5. PSYNC命令的实现

PSYNC命令的调用方式有两种:

1). PSYNC ? -1,表示主从服务器之间会执行完整重同步操作;

2). PSYNC <runid> <offset>,通过这两个参数,判断主从服务器之间执行何种操作;

服务器的回复方式有三种:

1). +FULLRESYNC <runid> <offset>,表示主服务器与从服务器之间会执行完整重同步操作;

2). +Continue,表示主服务器和从服务器之间执行部分重同步操作;

3). -ERR,表示主服务器的Redis版本低于2.8,不能识别PSYNC命令,从服务器会改为向主服务器发送SYNC命令,并于主服务器执行完整重同步操作;

6. 复制的实现

步骤1:记录主服务器的IP地址和端口号

从服务器会将客户端SLAVEOG命令发送的主服务器的IP地址和端口号记录在redisServer结构的masterhost和masterport属性中;当属性设置完毕后,从服务器则会向客户端回复OK,表示复制命令已经接收,而复制的实际操作则是在OK发送之后执行,因此SLAVEOF命令是一个异步命令;

步骤2:创建套接字连接

从服务器根据主服务器的IP地址和端口号,创建连向主服务器的套接字连接;套接字创建成功后,从服务器会为这个套接字绑定专门用于处理复制工作的文件事件处理器,用于执行后续接收RDB文件、接收主服务器发送的写命令等操作;主服务器在接收从服务器的套接字连接之后,将为该套接字创建相应的客户端的状态,将从服务器视作自己的客户端来处理;

步骤3:发送PING命令

套接字连接成功后的第一件事是从服务器向主服务器发送PING命令,原因有二:

1). 主从服务器连接成功后,还未进行过任何通信,通过发送PING命令可以检查套接字的读写状态是否正常;

2). 通过发送PING命令,可以检查主服务器是否能正常处理命令请求;

主服务器的回复方式则有三种:

1). 如果主服务器向从服务器返回一个命令回复,但是从服务器不能在规定的时间内读取回复的内容,则表示主从服务器之间的网络状态不佳,从服务器会断开连接并且重新创建连向主服务器的套接字;

2). 如果主服务器回复一个错误,表示主服务器没法处理从服务器的命令请求,不能继续执行复制的后续工作,当出现这种情况,从服务器会断开连接并且重新创建连向主服务器的套接字;

3). 主服务器回复"PONG"命令,表示主从服务器之间的网络连接状态正常,主服务器可以正常处理从服务器发送的命令请求,可以继续执行复制的后续工作;

步骤4:身份验证

当从服务器收到主服务器发送的PONG命令回复后,下一步要做的便是决定是否进行身份验证:如果从服务器设置了masterauth选项,则会进行身份验证;如果从服务器没有设置masterauth选择,则不会进行身份验证;进行身份验证时,从服务器会向主服务器发送AUTH命令,命令的参数则为从服务器masterauth选项的值;

从服务器在进行身份验证时会出现以下几种情况:

1). 如果主服务器没有设置requirepass选项,并且从服务器没有设置masterauth选项,则不需要进行身份验证的操作;

2). 如果从服务器发送的密码和主服务器requirepass选项的值相同,则验证通过,可以继续执行复制的后续操作;反之,如果值不同,则主服务器会返回一个invalid password错误;

3). 如果主服务器设置了requirepass选项,但是从服务器没有设置masterauth选项,则主服务器会返回一个NOAUTH错误;如果主服务器没有设置requirepass选项,而从服务器设置了masterauth选项,则主服务器会返回一个no password is set错误;

所有错误情况都会导致复制终止,重新创建套接字执行复制任务,直到身份验证通过;

步骤5:发送端口信息

身份验证通过后,从服务器会向主服务器发送REPLCONF listening-port <port-number>,告知主服务器从服务器的监听端口号;从服务器接收到该命令后,会将端口号记录在从服务器对应的redisClient结构的slave_listening_port属性中;而该属性的唯一作用即是在主服务器执行INFO replication命令时,打印出从服务器的端口号;

步骤6:同步
步骤7:命令传播

7. 心跳检测

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>,replication_offset是从服务器当前的复制偏移量;

该命令的作用主要体现在三方面;

1). 检测主从服务器之间的网络连接状态是否正常;

2). 辅助实现min-slaves选项值;

3). 检测是否出现命令丢失;

7.1 检测网络连接状态是否正常

如果主服务器超过一秒钟还没有收到从服务器发送的REPLCONF ACK命令,则主服务器知道主从服务器之间的网络连接状态出现异常;

7.2 辅助实现min-slaves选项

Redis的min-slaves主要包括min-slaves-to-write和min-slaves-max-lag两个选项值,通过这两个选项可以保证主服务器在安全的情况下才执行写命令;

1 min-slaves-to-write 3
2 min-salves-max-lag 10

如上,则表示当从服务器的数量小于3个,或者从服务器的延迟大于等于10s时,主服务器将拒绝执行写命令;

7.3 检测命令丢失

如果因为网络故障,主服务器发送给从服务器的写命令在半路丢失,则从服务器发送的REPLCONF ACK命令中携带的复制偏移量和主服务器的复制偏移量会不同,主服务器则会在复制积压缓冲区缓冲区中寻找,并将丢失的写命令重新发送给从服务器;

posted on 2022-12-28 10:47  VaeSSAQ  阅读(7)  评论(0)    收藏  举报