上一篇写了Redis复制功能的简单应用,下面我们看下Redis复制功能的实现过程。下面基本上是理论部分,枯燥乏味,但希望大家能看看,毕竟知识不都是感兴趣的.
耐得住寂寞,经得起诱惑,方能守得住繁华 ~.~
旧版复制功能的实现
Redis的复制功能分为同步和命令传播两个操作:
同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。
命令传播操作则用于在主服务器 的数据库状态被修改,导致从服务器的数据库状态出现不一致时,让主服务器的数据库重新回到一致状态。
从服务器对主服务器的同步操作需要通过向主服务发送sync命令来完成,以下是sync命令的执行步骤:
(1)从服务器向主服务器发送SYNC命令
(2)收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录现在开始执行的所有写命令。
(3)当主服务器的BGSAVE命令执行完毕时,主服务器会BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
(4)主服务器将记录在缓冲区的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
命令传播:当主服务器执行客户端写命令时,主服务器的数据库就有可能被修改,并导致主从不一致。此时主服务器会将自己执行的写命令发送给从服务器执行,当从服务器执行了相同的写命令后,主从服务器再次回到一致状态。
缺陷:
初始复制从服务器从来没有复制过任何主服务器或者从服务器当前要复制的主服务器和上次复制的主服务器不同。
断线后重复制:处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动连接从新连上主服务器,并继续复制。
新版复制功能的实现(PSYNC代替SYNC)
PSYNC命令具有完整重同步和部分重同步两种模式:
(1)完整重同步用于处理初次复制功能,与SYNC功能基本一致;
(2)部分重同步用于处理断线后重复值的情况,解决旧版效率低的问题。
部分重同步的实现:
(1)主服务器与从服务器都会维护一个复制偏移量
(2)复制积压缓冲区是由主服务器维护的一个固定长度先进先出的队列默认(1MB),发生断线从连时,但从服务器重连上主服务器,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器根据这个复制偏移量来决定对从服务器执行何种同步操作:如果offset偏移量之后的数据仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步,反之,执行完整重同步操作。除了复制偏移量和复制积压缓冲之外,实现部分重同步还需要用到服务器运行ID:每个Redis服务器都有自己启动时生成的由40个随机16进制字符组成的运行ID。当服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。当服务器断线重连时,从服务器向主服务器发送保存的运行ID,如果ID一样,则主服务器尝试部分重同步操作,如果不同,则执行完整重同步操作。
PSYNC命令的实现
PSYNC命令的实现方法有两种:
(1)如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始新的复制时将向主服务器发送PSYNC? -1 命令,主动请求进行完整重同步。
(2)如果之前复制过某个主服务器,那么从服务在开始一次新的复制时向主服务器发送PSYNC
(3)如果主服务器返回+FULLRESYNC回复,则表示主服务器将与从服务器执行完整重同步。
(4)如果主服务器返回+CONTINUE回复,则表示部分重同步,从服务器只需等待接收数据即可。
(5)主服务器返回-ERR回复,则表示主服务器版本低于2.8不识别PSYNC命令从服务器将向主服务器发送SYNC命令完成同步操作。
复制的实现
执行SLAVEOF ip port命令,此时从服务器首先将ip与端口保存到服务器状态的masterhost属性与masterport属性里面,并向客户端返回“OK”,表示命令已经被接收。
建立套接字连接
从服务器根据ip与端口创建连向主服务器的套接字,如果套接字连接到主服务器,那么从服务器将为这个套接字关联一个 专门用于处理复制工作的文件事件处理器,这个事件处理器负责执行后续复制工作。主服务器在接受从服务器的套接字连接后,将为该套接字创建相应的客户端状态,并将从服务器看作一个连接到主服务器的客户端对待,
发送PING命令
作用:
(1)虽然与主服务器建立套接字连接,但双方并未使用该套接字进行任何通信,检查套接字读写是否正常。
(2)检查主服务器是否能够正常处理命令请求。
发送命令后可能遇到的三种情况:
(1)超时,在规定的时间限制内从服务器未收到回复内容,此时从服务器断线重连。
(2)如果主服务器向从服务器回复一个错误,表示主服务器暂时无法处理从服务器的命令请求,从服务器断线重连。
(3)收到正常回复内容,则可以进行下一步操作。
身份验证(如果从服务器设置了masterauth选项)
从服务器向主服务器发送一条AUTH命令,此时从服务器可能遇到的情况有:
(1)主服务器没有设置requirepass选项,并且从服务器没有设置master选项,那么从服务器将继续执行从服务发送的命令,复制操作继续。
(2)如果从服务器通过AUTH命令发送的密码与主服务器requirepass设置的密码相同,那么主服务器将继续执行从服务器发送的命令,如果不同则主服务器返回一个invalid password错误。
(3)如果主服务器设置了requirepass选项,但从服务器没有设置masterauth选项,那么主服务器将返回一个NOAUTH选项。另一方面如果主服务器没有设置requirepass选项,但服务器设置了masterauth选项,那么主服务器将返回一个no password is set 错误。
发送端口信息,从服务器将执行REPLCONF listening-port port命令,向主服务器发送从服务器的监听端口号,主服务器将端口号保存在对应的客户端状态slave_listening_port属性中。
同步
命令传播
主服务器将自己执行的写命令发送给从服务器,从服务器只要一直执行主服务器发来的命令即可。
心跳检测
在命令传播阶段,从服务器默认以每秒一次的频率向主服务器发送命令:
REPLCONF ACK
作用:检测主服务器的网络连接状态;辅助实现min-slaves选项;检测命令丢失。
Redis的min-slaves-to-write和min-slaves-max-lag两个选项防止主服务器在不安全的情况下执行写命令。
当从服务器小于min-slaves-to-write或者min-slaves-to-write个数量的服务器延迟lag值都大于等于min-slaves-max-lag时,主服务器将拒绝执行写命令。
如果因为网络故障,主服务器传播给从服务器的写命令半路丢失,那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的偏移量,主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。
每天学一点,总会有收获。
下一步我们看下Redis的Sentinel(哨兵)
说明:尊重作者知识产权,文中内容参考《Redis设计与实现》,仅在此做学习与大家分享。