主从复制
从本篇文章开始记录主从复制相关的内容,主从复制相关的知识点包括 同步和命令传播、SYNC优化 两个部分。
用户可以通过SLAVEOF
命令让一个服务器去复制另一个服务器,被复制的服务器称为Master,本服务器称为Slave。进行复制中的主从服务器双方的数据库状态会保持一致,称为数据库状态一致性。
1. 同步和命令传播
Redis复制功能分为同步(sync)和命令传播(command propagate)。同步操作是由从服务器发起,将从服务器数据库状态更新至主服务器当前所处的状态;命令传播操作由主服务器发起,当主服务器的数据库状态被修改时,会导致主从服务器数据库状态会不一致,命令传播操作让主从服务器数据库状态重新回到一致状态。
1.1. 同步(SYNC)
从服务器客户端向主服务器发送SLAVEOF命令时,从服务器首先要执行同步操作,同步操作需要从服务器向主服务器发送SYNC命令来完成。
SYNC命令的执行步骤:
- 从服务器向主服务器发送SYNC命令
- 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
- 当主服务器BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
- 主服务器将记录在缓存区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
1.2. 命令传播(command propagate)
同步操作执行完毕之后,主从服务器两者的数据库将达到一致状态,但这种一致状态不是一层不变的,当主服务器再次发生数据变化时,主服务器需要对从服务器执行命令传播操作,把写操作相关的命令发送给从服务器。
当主服务器执行一个写命令或删除命令之后,主从服务器的数据库状态将会不一致,为了让主从服务器数据库状态保持一致,主服务器需要对从服务器执行命令传播操作,将自己执行的那条命令发送给从服务器执行。
2. SYNC优化
同步命令SYNC是由从服务器发起的,从服务器对主服务器的复制可以分为两种情况,初次复制和断线后重复制。
初次复制:从服务器之前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上次复制的主服务器不同呢。断线后重复制:处于命令传播阶段的主从服务器因为网络原因而中断了复制,但从服务器通过自动重连重新连接上了主服务器,并继续复制主服务器。
2.1. 断线后重新复制
当从服务器断线又重新连接上主服务器后,主从服务器数据库状态已经不一致,从服务器会向主服务器发送SYNC命令,主服务器会将RDB文件发送给从服务器,从服务器接收并载入RDB文件更新自己的数据库。Redis2.8版本之前的缺陷在于断线后重新复制,需要重新生成RDB文件,断线之前的复制工作浪费掉了。
事实上,
SYNC
命令是一个非常耗费资源的操作,只有在真正需要的时候才会执行SYNC
命令。每次执行SYNC命令,主从服务器需要执行以下动作:
- 主服务器需要执行BGSAVE命令来生成RDB文件,这个生成操作会耗费主服务器大量CPU、内存和磁盘IO资源
- 主服务器需要将自己生成的RDB文件发送给从服务器,这个发送操作会耗费主从服务器大量网络资源,并对主服务器响应命令请求的时间产生影响
- 接收到RDB文件的从服务器需要载入主服务器发来的RDB文件,并且在载入期间,从服务器因阻塞而没办法处理命令请求
针对SYNC
命令带来的资源耗费问题,Redis2.8版本之后,进行了改进,使用PSYNC
命令代替了SYNC
命令来执行复制时的同步操作。
PSYNC
命令具有完整重同步(full resynchronization)和部分重同步(partial resynchronization两种模式。full resynchronization用于处理初次复制情况,步骤和SYNC
命令执行步骤基本一样;partial resynchronization用于处理断线后重复复制情况,主服务器只需要将主从服务器连接断开期间执行的写命令发送给从服务器。
2.2. 部分重同步
部分重同步功能包含三个部分,分别是主服务器复制偏移量和从服务器复制偏移量、主服务器的复制积压缓冲区服务器的运行ID。
主服务器和从服务器分别维护一个复制偏移量。
复制积压缓冲区是由主服务器维护的一个固定长度的队列,默认大小为1MB。
服务器运行ID:每个Redis服务器不论主从,都会有自己的运行ID,由40个随机的十六进制字符组成。
2.3. 复制的实现
通过向服务器发送SLAVEOF
命令,可以让一个从服务器去复制一个主服务器。SLAVEOF
命令是一个异步命令,执行SLAVEOF
命令后,复制过程如下:
- 设置主服务器的地址和端口号:将主服务器的IP地址和断开保存到服务器状态的masterhost属性和mastesrport属性。
- 建立套接字连接: 从服务器可以看作主服务器的客户端。
- 发送PING命令:从服务器成为主服务器的客户端后,向主服务器发送一个
PING
命令。- 身份验证:从服务器收到主服务器返回的“PONG”回复后,进行身份认证。
- 发送端口信息:向主服务器发送从服务器监听的端口号。
- 同步:从服务器向主服务器发送
PSYNC
命令,执行同步操作。- 命令传播:完成同步之后,主从服务器就会进入命令传播阶段。
3. 心跳检测
主服务器通过向从服务器发送传播命令来更新从服务器状态,从服务器通过向主服务器发送 REPLCONF ACK 进行心跳检测和命令丢失检测。从服务器默认会以每秒一次的频率向主服务器发送命令REPLICONF ACK <replication_offset>
, <replication_offset>
为从服务器当前的复制偏移量。
发送 REPLCONF ACK 命令对于主从服务器有三个作用,分别是检测主从服务器的网络连接状态、辅助实现min-slaves
选项、检测命令丢失。
4. 参考文献
[1] 黄健宏.Redis设计与实现[M].北京:机械工业出版社
[2] https://www.bilibili.com/video/BV1S54y1R7SB?from=search&seid=15572804586770683866