Redis设计与实现-11.复制
Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让服务器去复制另一台服务器,被复制的为master服务器,复制的被成为slave服务器。
旧版本Redis实现复制
- 同步:将从服务器的数据库状态更新至主服务器的数据库状态。
- 命令传播:主服务器的数据库状态被修改,导致主从服务器数据库状态出现了不一致,让主从服务器的数据库重新回到一致。
同步
从服务器对主服务器的同步操作需要通过想主服务器发送SYNC命令完成:
- 从服务器想主服务器发送SYNC命令
- 收到命令的主服务器执行BGSAVE命令,生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
- 当主服务器BGSAVE执行完毕后,主服务器将RDB文件发送给从服务器,从服务器载入RDB文件,更新从数据库状态。
- 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,更新数据库状态。
命令传播
在同步操作执行完毕之后,从服务器暂时达到了和主服务器数据库一致的状态,但是一旦对主服务器做了其他写操作,那么服务器状态就会不一致。解决这个问题的办法就是当主服务器执行了一个写命令的时候,主服务器会该条命令发送给从服务器。
旧版功能缺陷
Redis2.8之前,复制分为一下两种情况:
- 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同。
- 断线后复制:处于命令传播阶段的主从服务器因为网络问题中断了复制,但从服务器通过自动重连重新连接主服务器,并继续复制主服务器。
对于以上两种情况,初次复制可以很好的完成任务,但是断线后复制,就会存在如下问题:当主从服务器断开连接的时候,主服务器进行了写入,就会导致主从不一致,从服务器向主服务器发送SYNC命令,这个时候主服务器又将进行BGSAVE命令,这样非常低效。
新版本复制功能实现
Redis2.8版本之后采用PSYNC命令代替SYNC命令。PSYNC命令具有完整重同步和部分重同步了两种模式。完整重同步和初次复制的情况一样,而部分重同步则解决了断线后复制会重新生成RDB文件的问题。
部分重同步的实现
部分重同步的实现由三个部分组成,分别是:主服务器的复制偏移量和从服务器的复制偏移量,服务器的复制挤压缓冲区,服务器的运行ID
复制偏移量
在执行复制的双方,会分别维护一个复制偏移量。
- 主服务器每次向从服务器发送N个字节的数据时候,就会将自己的复制偏移量+N
- 从服务器每次收到主服务器传播来的N字节的数据的时候,就会将自己的复制偏移量+N
对比双方的复制偏移量,我们就可以直到服务器的状态是否处于一致,如果一致,那么双方的复制偏移量总是相同的
复制挤压缓冲区
复制挤压缓冲区是主服务器威化的一个固定长度的FIFO队列,默认为1MB。当主服务器进行命令传播的时候,不仅会将写命令发送给从服务器,还会将写命令写入到复制挤压缓冲区里面。并且复制挤压缓冲区还会对每个字节的记录相应的复制偏移量。
当从服务器重新连接主服务器的时候,从服务器会通过PSYNC命令将自己的复制偏移量发给主服务器,主服务器通过偏移量判断执行如下操作:
- 如果offset偏移量之后的数据仍然存在于复制挤压缓冲区中,那么执行部分重同步。
- 如果offset偏移量之后的数据已经不存在于复制挤压缓冲区中,那么执行完整重同步。
服务器运行ID
每个运行的redis服务器都有自己的ID,代表自己的唯一性。当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID发送给从服务器,从服务器也会将其保存起来。当从服务器断线重连后,从服务器会将之前保存的主服务器的运行ID发送过去:
- 如果ID相同,说明之前的复制就是这台主服务器,尝试判断是否部分重同步还是完整重同步。
- 如果ID不同,要执行完成重同步。