Redis设计与实现—复制原理
前言
Redis 中的复制命令原理
@
Redis中可以通过 SLAVEOF 命令来设置一个服务器为从服务器,从而复制指定的主服务器的数据。
一、旧版复制原理
旧版复制功能包括 同步(sync) 和 命令传播 两个操作。
1.1 同步
从服务器对主服务器的同步操作需要发送 SYNC 命令来实现,该命令的执行步骤包括:
- 从服务器向主服务器发送SYNC命令;
- 主服务器接收到命令后,执行
BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区来记录从当前开始的写命令; - 当主服务器的
BGSAVE命令执行完成后就将RDB文件发送给从服务器,从服务器开始接受并且载入这个RDB文件; - 主服务器将记录在缓冲区里面的所有写命令发送给从服务器执行,从服务器来将自己的数据库状态更新为主服务器的状态。
1.2 命令传播
在同步操作完成后,主服务器一旦有一些新命令的执行导致数据库发生改变就需要将改变立刻同步到从服务器,因此对于会导致数据库更改的命令,主服务器会同样发送到从服务器。
1.3 旧版复制的缺陷
在Redis中的复制可以分为初次复制和断线后的重复制。旧版复制对于初次复制没有问题,但是断线后的重复制效率比较低。
因为当复制过程中出现断线后,当从服务器再次连接上时由于没有记录上次的复制位置,所以需要重新从头开始复制,执行 SYNC 命令(初次复制)。
但是主从服务器断开的时间比较短,导致主服务器在断线期间执行的写命令较少,而每次断线时为了这点命令选择执行 SYNC 命令,效率非常低效。
SYNC 命令是一种非常耗时的操作
- 主服务器要执行
BGSAVE指令来生成RDB文件,这个操作会消耗主服务器大量的 CPU、内存和磁盘 I/O 资源; - 主服务器将
RDB文件发送到从服务器会消耗网络资源‘ - 从服务器接受
RDB文件后会载入,这个期间从服务器会阻塞没有办法处理命令请求。
二、新版复制原理
新版复制采用 PSYNC 命令代替旧版的 SYNC 命令。
该命令包含两个部分:
- 完整重同步:和旧版复制的初次复制操作一样。
- 部分重同步:用于断线后的处理,采用 复制偏移量、复制积压缓冲区、服务器运行ID来实现搞笑的重连接复制操作。
2.1 部分重同步
2.1.1 复制偏移量

主服务器和从服务器各保存一份复制偏移量,每次主服务器发送数据和从服务器成功接受数据就增加相应的偏移量。通过复制偏移量能够确保主从服务器的数据状态是否一致。
2.1.2 复制积压缓冲区

复制积压缓冲区的特点:
- 复制积压缓冲区是由主服务器维护的一个固定长度的先进先出队列,默认大小为 1MB。
- 每当主服务器进行命令传播时,它不仅会将数据发送给从服务器,还要将数据写入到复制积压缓冲区中,复制积压缓冲区同时还会为队列中的每个字节记录相应的复制偏移量。
- 那么当从服务器断线重连后,它会将自己的复制偏移量发送给主服务器,主服务器就会检查该偏移量之后的数据是否全部存在于复制积压缓冲区中,如果全部存在就执行部分重同步操作;否则必须执行完整重同步操作。
复制积压缓冲区大小可以动态调整:
- 默认大小是 1MB,但是如果断线时间过长,断线时写入的数据较多,就会由于缓冲区大小太小而不得不进行完整重同步操作。
- 缓冲区大小的计算公式:
second * write_size_per_second来估算,second表示从服务器断线后重新连接上主服务器所需的时间;write_size_per_second表示主服务器每秒钟写入的命令数据量。 - 可以通过修改配置文件中的
repl-backlog-size选项调整缓冲区大小;通过repl-backlog-ttl调整缓冲区的存活时间,超过会被销毁。
2.1.2 服务器ID
每个服务器启动时都会分配一个服务器ID,当从服务器对主服务器进行初次同步操作时,主服务器会将自己的服务器ID发送给从服务器,从服务器会保存起来。
如果从服务器断开后重连到一个主服务器,它会发送之前保存的主服务器ID,如果ID和当前主服务器ID相同就执行部分重同步操作,否则执行完整重同步操作。
2.2 PSYNC 命令的实现
从服务器发送指令:
- 如果从服务器初次同步主服务器,它会发送
PSYNC ? -1命令,请求完整重同步操作; - 如果从服务器已经复制过某个主服务器后,当它开始一次新复制时会发送
PSYNC <runid> <offset>命令,runid是上次主服务器的ID,offset是自己当前的复制偏移量。
主服务器回复指令:
+FULLRESYNC <runid> <offset>:主服务器与从服务器执行完整的重同步操作;+CONTINUE:执行部分重同步操作,等待主服务器发送数据;-ERR:识别不了命令,出现问题。
三、复制的实现
3.1 从服务器保持主服务器的地址和端口
当客户端向从服务器发送 slave of xxx xxx 命令时,从服务器将主服务器的 ip地址和 port端口保存到 redisServer 中的 masterhost 和 masterport 属性中。
3.2 建立套接字连接
从服务器建立套接字来连接到主服务器,如果连接成功,从服务器会为这个套接字关联一个专门用于处理复制工作的文件事件处理器,这个处理器将负责执行后续的复制工作。
主服务器在接受从服务器的套接字连接后,将为该套接字建立客户端状态,并将从服务器看作是一个连接到主服务器的客户端来看待。
3.3 发送PING命令
从服务器发送一个 PING 命令来保证主从服务器双方读写状态是否正常。
3.4 身份认证
如果主服务器设置了 requirepass 并且从服务器设置了 masterauth,需要进行身份认证。
3.5 发送端口信息
身份认证后,从服务器会执行命令 REPLCONF listening-port <port-number> 来向主服务器发送自己监听的端口号方便后续通信。
主服务器接受后会保存到 slave_listening_port 属性中。
3.6 同步
从服务器向主服务器发送 PSYNC 命令,执行同步操作,并将自己的数据库更新。
注意在同步操作前只有从服务器是主服务器的客户端,而同步操作以后主从服务器都是对方的客户端和服务器,因为它们可以向对方发送命令请求或者互相向对方回复命令。
3.7 命令传播
一直执行,保证双方数据同步。
四、心跳检测
发生在命令传播阶段,从服务器会每隔1s 向主服务器发送命令 REPLCONF ACK <replication_offset> ,其中 replication_offset 是从服务器当前的复制偏移量。
心跳检测的作用
1) 检测主从服务器的网络状态
如果主服务器超过1s 没有接收到从服务器的 REPLCONF ACK 命令,就知道之间的连接出现了问题。可以通过 info replication 命令来查看:

lag 表示距离上一次接收到从服务器的心跳检测过去了多长时间。
2) 辅助实现 min-slaves配置选项
min-slaves-to-write:从服务器的最少数量,少于这个标准主服务器会拒绝执行写命令;
min-slaves-max-lag:心跳延迟最大值。
3) 检测命令丢失
由于从服务器发送时会携带上自己的复制偏移量,主服务器就可以在接受心跳检测时检查自己的复制偏移量是否同步,如果发现从服务器的复制偏移量小了,就将复制积压缓冲区里面的数据发送给从服务器。

浙公网安备 33010602011771号