Redis-复制

复制功能的实现

PSYNC命令具有完整重同步和部分重同步两种模式:

  • 完整重同步
    用于处理初次复制情况,完整重同步通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里的写命令来同步
  • 部分重同步
    用于处理断线后重复制情况,当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接受并执行这些写命令就可以将数据库更新至主服务器当前所处的状态

部分重同步的实现

部分重同步功能由以下三个部分构成:

  • 主服务器的复制偏移量和从服务器的复制偏移量
  • 主服务器的复制挤压缓冲区
  • 服务器的运行ID

复制偏移量

执行复制的双方,主服务器和从服务器会分别维护一个复制偏移量:
主服务器每次向从服务器传播N个字节的数据,就将自己的复制偏移量的值加上N,从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N

通过对比主从服务器的复制偏移量,程序可以很容易地知道主从服务器是否处于一致状态:

  • 如果主从服务器的偏移量是相同的,那么它们处于一致状态
  • 如果主从服务器的偏移量并不相同,那么说明主从服务器并未处于一致状态

复制积压缓冲区

复制积压缓冲区是由主服务器维护的一个固定长度先进先出队列,默认大小为1MB

当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制挤压缓存里面

主服务器的复制积压缓冲区中会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中俄每个字节记录相应的复制偏移量

当从服务器重新连接上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发给主服务器,主服务器会根据这个偏移量来决定从服务器执行何种同步操作

  • 如果从服务器的offset偏移量之后的数据仍然存在于主服务器的复制积压缓冲区,那么主服务器将对从服务器执行部分重同步操作
  • 如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作

服务器运行ID

每个Redis服务器,不论主服务器还是从服务器,都会有自己的运行ID,每个运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传给从服务器,而从服务器则会将这个运行ID保存起来.当从服务器断线并连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的ID

  • 如果从服务器保存的ID和当前连接的主服务器的运行ID相同,那么说明从服务器短线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作
  • 相反的,如果从服务器之前保存的ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器会对从服务器执行完整重同步操作

复制的步骤

通过向从服务器发送SLAVEOF命令,可以让一个从服务器去复制一个主服务器

步骤1:设置主服务器的地址和端口

首先,从服务器客户端向从服务器发送命令:

1
2
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK

从服务器首先要做的是将客户端给定的主服务器IP和端口号保存到服务器状态中,之后向客户端返回OK,表示复制指令已经被接收,而实际的复制工作将在OK返回之后才真正开始执行

步骤2:简历套接字链接

在SLAVEOF命令执行之后,从服务器将根据命令所设置的IP地址和端口,创建联想主服务器的套接字链接

如果从服务器创建的套接字能成功连接到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的事件处理器,这个处理器负责执行后续的复制工作,比如接受RDB文件,以及接受主服务器传来的写命令,诸如此类

而主服务器在接受从服务器的套接字连接之后,将为该套接字创建响应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来看待.因为复制工作接下来的几个步骤都会以从服务器向主服务器发送命令请求的形式来进行,所以"从服务器是主服务器的客户端"

步骤3:发送PING命令

从服务器成为主服务器的客户端之后,要想主服务器发送一个PING命令

这个PING命令有两个作用:

  • 虽然主从服务器成功建立起了套接字连接,但是双方并未使用该套接字进行过任何通信,通过发送PING命令可以检查套接字的读写状态是否正常
  • 因为复制工作接下来的几个步骤都必须在主服务器可以正常处理命令请求的状态改下才能进行,通过PING命令可以检查主服务器能否正常处理命令请求

步骤4:身份验证

如果从服务器设置了masterauth选项,那么进行身份验证

从服务器向主服务器发送一条AUTH命令,命令的参数为服务器masterauth选项的值

  • 如果主服务器没有设置requirepass选项,而且从服务器没有设置masterauth选项,那么主服务器将只需执行从服务器发送的命令,复制工作可以继续进行
  • 如果从服务器通过AUTH发送的密码和主服务器requirepass选项所设置的密码相同,那么主服务器将继续执行从服务器发送的命令,复制工作可以进行;与此相反,如果从服务器设置的密码不相同,那么主服务器将返回一个invalid password错误
  • 如果主服务器设置了requirepass选项,但从服务器没有设置masterauth选项,那么主服务器将返回一个NOAUTH错误;如果主服务器没有设置requirepass选项,但是从服务器设置了masterauth选项,那么主服务器将返回一个no password is set错误

步骤5:发送端口信息

在身份验证步骤之后,从服务器向主服务器发送从服务器的监听端口号
主服务器在接收从服务器的监听端口号之后,会将端口号记录在从服务器所对应的客户端状态属性中

步骤6:同步

从服务器将向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库更新至主服务器数据库当前所处的状态

步骤7:命令传播

当完成了同步之后,主从服务器将会进入命令传播阶段,这时主服务器只要一直将自己执行的写命令发送给从服务器,而从服务器只要一直接收并执行主服务器发来的写命令.就可以保证主从服务器一直保持一致了

心跳检测

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK {replicationoffset},其中{replicationoffset}是从服务器当前的复制偏移量
心跳检测对主从服务器有三个作用:

  • 检测主从服务器的网络连接状态
  • 辅助实现min-salves选项
    Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令.例子:如果min-slaves-to-write设为3,min-slaves-max-lag设为10,则在从服务器的数量小于3,或者三个从服务器的延迟(lag)值都大于等于10秒时,主服务器将拒绝执行写命令
  • 检测丢失命令

重点回顾

  • 部分重同步通过复制偏移量,复制积压缓冲区,服务器ID三个部分来实现
  • 主服务器通过向从服务器传播命令来更新从服务器的状态,保持主从服务器一致,而从服务器则通过向主服务器发送命令来进行心跳检测,以及命令丢失检测