Redis复制功能

Redis的复制功能分为同步(Sync)和命令传播(Command Propagate)两个操作,其中

  • 同步:用于将从服务器的数据库状态更新值主服务器当前所处的数据库状态
  • 命令传播:用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态

旧版复制功能的缺陷

从服务器对主服务器的复制可以分为两种

  • 初次复制:此时从服务器没有复制过任何的主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同

  • 断线后重新复制:在命令传播阶段的主从服务器因为网络等问题断开了连接而中断了复制,但是从服务器通过自动重连机制重新连接上了主服务器,并继续复制,这种情况下的复制效率较低

    当中断重连之后,从服务器会继续向主服务器发送sync命令,那么主服务器会再次执行BGSAVE命令,将当前主服务器的数据库状态给快照出来,发送给从服务器,从服务器因为不知道是从哪里中断的,那么只能从头开始载入RDB文件

新版复制功能的实现

为了解决旧版复制功能的缺陷,Redis2.8开始新版复制功能使用PSYNC命令代替SYNC命令来执行复制时的同步操作,PSYNC具有完整重同步和部分重同步两种模式

  • 完整重同步:与旧版初次复制一样
  • 部分重同步:如果在同步的过程中,主从服务器的连接断开了,主服务器可以将主从服务器断开期间执行的写命令发送给从服务器,从服务器只要接受并执行这些写命令就可以将数据库状态更新至主服务器当前所处的状态了

部分重同步的实现

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

  • 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量
  • 主服务器的复制积压缓冲(replication backlog)
  • 服务器的运行ID(run ID)

复制偏移量

主服务器和从服务器会分别维护一个复制偏移量

  • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N

  • 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N

这样通过对比主从服务器的复制偏移量即可以知道主从服务器的状态是否一致

复制积压缓冲

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

  • 固定长度的队列
    • 与普通队列的出队入队方式一样,都是先进先出,但是该队列的长度是固定的,意味着如果当前元素长度超过了队列的长度,最先入队的元素会被弹出,然后将新元素入队

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

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

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

服务器运行ID

每个Redis服务器都会有自己的运行ID,运行ID在服务器启动时自动生成

  • 当从服务器对主服务器进行初次复制时,主服务器会把自己的运行ID发送给从服务器,从服务器会将之保存起来
  • 当从服务器断线重连后,会将自己保存的运行ID发送给主服务器
    • 如果运行ID与接收方主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的主服务器,那么主服务器可以执行部分重同步
    • 如果运行ID不同,那么就表示当前连接的主服务器与断线前连接的主服务器不是一台服务器,那么就要执行完整重同步

PSYNC命令的实现

PSYNC的调用方法有两种

  • 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF NO ONE命令,那么从服务器在开始新的复制时将向主服务器发送PSYNC ? -1 命令,主动请求主服务器进行完整重同步
  • 如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC runid offset命令,其中,runid是上一次复制的主服务器的runid,offset是从服务器当前的复制偏移量

接收到PSYNC的主服务器会向从服务器返回以下三种回复的一种

  • 主服务器返回+FULLRESYNC runid offset,表示主服务器将与从服务器执行完整重同步
  • 主服务器返回+CONTINUE,表示主服务器将与从服务器执行部分重同步操作
  • 主服务器返回-ERR,表示当前主服务器的Redis版本低于2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令并与主服务器执行完整重同步