主从复制
主从复制分为全量复制和部分复制。
全量复制
场景
什么时候会发生全量复制呢?1)第一次复制,即主从结构刚搭建,从服务第一次请求同步信息时;2)当主从连接断开后,重新连接,从服务器发送的偏移量的数据已经不再主服务器中了,触发全量复制。
原理
从服务器发送命令psync,请求复制数据,主服务器会执行bgsave命令,生成RBD文件,之后把文件发送给从服务,从服务器载入文件,恢复数据库状态。在这个过程中(生成RBD文件,发送,接收,载入)如果主服务接收了写命令,会保存在缓冲区中,之后再把缓冲区的数据发送给从服务器。
部分复制
场景
主要是针对主从断线之后,重新连接后的数据同步文件。
原理
先了解三个部分:
- 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量。
- 主服务器的复制积压缓冲区(replication backlog)。
- 服务器的运行ID(run ID)。
复制偏移量
主从服务器都维护一个复制偏移量。
- 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N。
- 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N。
只要主从服务器的偏移量是一致的,那么数据就是一致的。
复制积压缓冲区
主服务器除了发送给从服务命令外,主服务会将写命令保存在积压缓冲区中,并记录复制偏移量。注意:积压缓冲区是一个固定大小的队列。如果队列满后,会移除最老的数据。


当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器,主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:
- 如果offset偏移量之后的数据(也即是偏移量offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主服务器将对从服务器执行部分重同步操作。
- 相反,如果offset偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作。
服务运行ID
用来确认主服务器的。从服务器将服务运行id发送给主服务器。主服务器进行确认。
- 如果从服务器保存的运行ID和当前连接的主服务器的运行ID相同,那么说明从服务器断线之前复制的就是当前连接的这个主服务器,主服务器可以继续尝试执行部分重同步操作。
- 相反地,如果从服务器保存的运行ID和当前连接的主服务器的运行ID并不相同,那么说明从服务器断线之前复制的主服务器并不是当前连接的这个主服务器,主服务器将对从服务器执行完整重同步操作。
PSYNC命令原理
当第一次复制时,从向主发送psync ? -1,请求全量复制。重新连接后,从向主发送psync 主的runid 从的复制偏移量。
根据情况,接收到PSYNC命令的主服务器会向从服务器返回以下三种回复的其中一种:
- 如果主服务器返回+FULLRESYNC 回复,那么表示主服务器将与从服务
器执行完整重同步操作:其中runid是这个主服务器的运行ID,从服务器会将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset则是主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量。 - 如果主服务器返回+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步
操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了。 - 如果主服务器返回-ERR回复,那么表示主服务器的版本低于Redis 2.8,它识别不了
PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作。
为了熟悉PSYNC命令的用法,让我们来看一个完整的复制——网络中断——重复制例子。
首先,假设有两个Redis服务器,它们的版本都是Redis 2.8,其中主服务器的地址为
127.0.0.1:6379,从服务器的地址为127.0.0.1:12345。
如果客户端向从服务器发送命令SLAVEOF 127.0.0.1 6379,并且假设从服务器是第一次执
行复制操作,那么从服务器将向主服务器发送PSYNC ? -1命令,请求主服务器执行完整重同步操作。
主服务器在收到完整重同步请求之后,将在后台执行BGSAVE命令,并向从服务器返回
+FULLRESYNC 53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3 10086回复,其中
53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3是主服务器的运行ID,而10086则是主服务器当前的复制偏移量。
假设完整重同步成功执行,并且主从服务器在一段时间之后仍然保持一致,但是在复制偏
移量为20000的时候,主从服务器之间的网络连接中断了,这时从服务器将重新连接主服务
器,并再次对主服务器进行复制。
因为之前曾经对主服务器进行过复制,所以从服务器将向主服务器发送命令PSYNC
53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3 20000,请求进行部分重同步。主服务器在接收到从服务器的PSYNC命令之后,首先对比从服务器传来的运行
ID53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3和主服务器自身的运行ID,结果显示该ID和主服务器的运行ID相同,于是主服务器继续读取从服务器传来的偏移量20000,检查偏移量为20000之后的数据是否存在于复制积压缓冲区里面,结果发现数据仍然存在。
确认运行ID相同并且数据存在之后,主服务器将向从服务器返回+CONTINUE回复,表示将
与从服务器执行部分重同步操作,之后主服务器会将保存在复制积压缓冲区20000偏移量之后的所有数据发送给从服务器,主从服务器将再次回到一致状态。
命令传播
在同步完成之后,主服务器会定期发送命令给从服务器,这个阶段为命令传播阶段。
心跳检测
在命令传播阶段,从服务器会一秒一次的频率向主服务器发送命令:
REPLCONF ACK <replication_offset>
主要有以下几个作用:
- 确认和主服务器连接正常
- 检测命令丢失。这一点很重要,如果主服务器发送了命令,但是从没有接到。如果没有这各个检测方法。导致命令丢失了,因为只要主发送了命令,偏移量就增加,不顾从是否接收到。如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。
总结
- 部分重同步通过复制偏移量、复制积压缓冲区、服务器运行ID三个部分来实现。
- 在复制操作刚开始的时候,从服务器会成为主服务器的客户端,并通过向主服务器发送命令请求来执行复制步骤,而在复制操作的后期,主从服务器会互相成为对方的客户端。
- 主服务器通过向从服务器传播命令来更新从服务器的状态,保持主从服务器一致,而从服务器则通过向主服务器发送命令来进行心跳检测,以及命令丢失检测。
- 只有在第一次和重连时发送psync命令,之后主节点就从将同步数据发过来。所以之后的REPLCONF ACK <replication_offset>命令保证了从服务器是否已经收到命令。
