# 一、主从复制过程
当一个 Redis 节点(slave 节点)接收到类似 slaveof 127.0.0.1 6380
的指令后直至其可以从 master 持续复制数据,大体经历了如下几个过程:
# 1、保存master地址
当 slave 接收到 slaveof 指令后,slave 会立即将新的master 的地址保存下来。
# 2、建立连接
slave 中维护着一个定时任务,该定时任务会尝试着与该 master 建立 socket 连接。如果连接无法建立,则其会不断定时重试,直到连接成功或接收到 slaveof no one
指令。
# 3、slave 发送 ping 命令
连接建立成功后,slave 会发送 ping 命令进行首次通信。如果 slave 没有收到 master 的回复,则 slave 会主动断开连接,下次的定时任务会重新尝试连接。
# 4、对 slave 身份验证
如果 master 收到了 slave 的 ping 命令,并不会立即对其进行回复,而是会先进行身份验证。如果验证失败,则会发送消息拒绝连接;如果验证成功,则向 slave 发送连接成功响应。
# 5、master 持久化
首次通信成功后,slave 会向 master 发送数据同步请求。当 master 接收到请求后,会 fork出一个子进程,让子进程以异步方式立即进行持久化。
# 6、数据发送
持久化完毕后 master 会再 fork 出一个子进程,让该子进程以异步方式将数据发送给slave。slave 会将接收到的数据不断写入到本地的持久化文件中。
在 slave 数据同步过程中,master 的主进程仍在不断地接受着客户端的写操作,且不仅将新的数据写入到了master 内存,同时也写入到了同步缓存。当 master 的持久化文件中的数据发送完毕后,master 会再将同步缓存中新的数据发送给 slave,由 slave 将其写入到本地持久化文件中。数据同步完成。
# 7、slave 恢复内存数据
当 slave 与 master 的数据同步完成后,slave 就会读取本地的持久化文件,将其恢复到本地内存,然后就可以对外提供读服务了。
# 8、持续增量复制
在slave 对外提供服务过程中,master 会持续不断的将新的数据以增量方式发送给slave,以保证主从数据的一致性。
# 二、数据同步演变过程
# 1、sync 同步
Redis 2.8 版本之前,首次通信成功后,slave 会向 master 发送 sync 数据同步请求。然后 master 就会将其所有数据全部发送给 slave,由 slave 保存到其本地的持久化文件中。这个过程称为全量复制。
但这里存在一个问题:在全量复制过程中可能会出现由于网络抖动而导致复制过程中断。当网络恢复后,slave 与 master 重新连接成功,此时slave 会重新发送 sync 请求,然后会从头开始全量复制。
由于全量复制过程非常耗时,所以期间出现网络抖动的概率很高。而中断后的从头开始不仅需要消耗大量的系统资源、网络带宽,而且可能会出现长时间无法完成全量复制的情况。
# 2、psync 同步
Redis 2.8 版本之后,全量复制采用了 psync(Partial Sync,不完全同步)同步策略。在全量复制过程中出现由于网络抖动而导致复制过程中断时,当重新连接成功后,复制过程可以**“断点续传”**。即从断开位置开始继续复制,而不用从头再来。这就大大提升了性能。
为了实现 psync,整个系统做了三个大的变化:
# 1.复制偏移量
系统为每个要传送的数据进行了编号,该编号从 0 开始,每个字节一个编号。该编号称为复制偏移量。参与复制的主从节点都会维护该复制偏移量。
master 每发送过一个字节数据后就会进行累计。统计信息通过info replication
的master_repl_offset
可查看到。同时,slave 会定时向master 上报其自身已完成的复制偏移量给 master,所以 master 也会保存 slave 的复制偏移量 offset
。
slave 在接收到master 的数据后,也会累计接收到的偏移量。统计信息通过info replication
的 slave_repl_offset
可查看到。
# 2.主节点复制 ID
当master 启动后就会动态生成一个长度为 40 位的 16 进制字符串作为当前 master 的复制 ID,该 ID 是在进行数据同步时 slave 识别 master 使用的。通过 info replication
的 master_replid
属性可查看到该 ID。
# 3.复制积压缓冲区
当 master 有连接的 slave 时,在 master 中就会创建并维护一个队列 backlog,默认大小为 1MB,该队列称为复制积压缓冲区。master 接收到了写操作数据不仅会写入到 master 主存,而且还会写入到复制积压缓冲区。其作用就是用于保存最近操作的数据,以备“断点续传”时做数据补偿,防止数据丢失。
Redis的复制积压缓冲区是一个用于存储待复制数据的缓冲区,它的作用在于当主节点将数据同步到从节点时,如果从节点处理能力不足或者网络传输延迟,就会导致从节点无法及时处理主节点发送过来的数据。这时候,数据就会在复制积压缓冲区中等待,直到从节点有能力处理这些数据。
简单来说,复制积压缓冲区就是一个缓存区域,用于暂存主节点发送给从节点但还未被从节点处理的数据,保证了数据的可靠性和一致性。当从节点处理能力充足或者网络传输延迟降低时,数据就会被从缓冲区中取出并同步到从节点上。
# 4.psync 同步过程
psync 是一个由 slave 提交的命令,其格式为 psync <master_replid> <repl_offset>
,表示当前 slave 要从指定的 master 中的 repl_offset+1
处开始复制。repl_offset
表示当前 slave 已经完成复制的数据的offset。该命令保证了“断点续传”的实现。
在第一次开始复制时,slave 并不知道 master 的动态 ID,并且一定是从头开始复制,所以其提交的 psync 命令为 PSYNC ? -1
。即 master_replid
为问号(?),repl_offset
为-1。
如果复制过程中断后slave 与master 成功连接,则 slave 再次提交 psyn 命令。此时的 psyn命令的 repl_offset
参数为其前面已经完成复制的数据的偏移量。
其实,并不是slave 提交了psyn 命令后就可以立即从master 处开始复制,而是需要master给出响应结果后,根据响应结果来执行。master 根据 slave 提交的请求及master 自身情况会给出不同的响应结果。响应结果有三种可能:
● FULLRESYNC <master_replid> <repl_offset>
:告知 slave 当前 master 的动态 ID 及可以开始全量复制了,这里的 repl_offset
一般为 0
● CONTINUE
:告知 slave 可以按照你提交的repl_offset
后面位置开始“断点续传”了。
● ERR
:告知 slave,当前master 的版本低于 Redis 2.8,不支持 psyn,可以开始全量复制了。
# 5.pysync存在的问题
● 在 psync 数据同步过程中,若 slave 重启,在 slave 内存中保存的 master 的动态 ID 与续传 offset 都会消失,“断点续传”将无法进行,从而只能进行全量复制,导致资源浪费。
● 在 psync 数据同步过程中,master 宕机后 slave 会发生“易主”,从而导致 slave 需要从新 master 进行全量复制,形成资源浪费。
# 3、psync同步的改进
Redis 4.0 对 psync 进行了改进,提出了“同源增量同步”策略。
ChatGPT:
Redis的同源增量同步策略是指在Redis集群中,通过同一个源节点将增量数据同步到其他节点,以保证数据的一致性。 具体来说,同源增量同步策略包括以下步骤:
- 选择一个节点作为源节点:在Redis集群中,选择一个节点作为数据的源节点,该节点负责接收客户端的写操作,并将写操作的增量数据广播给其他节点。
- 接收并记录增量数据:源节点接收到客户端的写操作后,会将这些操作转化为增量数据,并将其记录下来。
- 广播增量数据:源节点将记录的增量数据广播给其他节点,以便其他节点能够更新自己的数据副本。
- 其他节点应用增量数据:接收到增量数据的其他节点会将其应用到自己的数据副本中,从而保持数据的一致性。
需要注意的是,同源增量同步策略只会同步增量数据,而不是全量数据。这样做可以减少网络传输的开销,并提高同步效率。同时,由于只有一个源节点,可以避免多个节点同时修改数据而导致的冲突问题。
总之,Redis的同源增量同步策略通过将增量数据从源节点广播给其他节点,以实现数据的一致性。这种策略在Redis集群中广泛应用,可以提高数据同步效率和系统的可靠性。
# 1.解决 slave 重启问题
针对“slave 重启时 master 动态 ID 丢失问题”,改进后的 psync 将 master 的动态 ID 直接写入到了 slave 的持久化文件中。
slave 重启后直接从本地持久化文件中读取 master 的动态 ID,然后向 master 提交获取复制偏移量的请求。master 会根据提交请求的slave 地址,查找到保存在 master 中的复制偏移量,然后向 slave 回复 FULLRESYNC <master_replid> <repl_offset>
,以告知 slave 其马上要开始发送的位置。然后master 开始“断点续传”。
# 2.解决 slave 易主问题
slave 易主后需要和新的 master 进行全量复制,本质原因是新 master 不认识 slave 提交的psync 请求中“原 master 的动态 ID”。如果 slave 发送 PSYNC <原 master_replid> <repl_offset>
命令,新master 能够识别出该slave 要从原master 复制数据,而自己的数据也都是从该master复制来的。那么新master 就会明白,其与该slave“师出同门”,应该接收其“断点续传”同步请求。
而新 master 中恰好保存的有“原 master 的动态 ID”。由于改进后的 psync 中每个 slave都在本地保存了当前master 的动态 ID,所以当 slave 晋升为新的 master 后,其本地仍保存有之前 master 的动态 ID。而这一点也恰恰为解决“slave 易主”问题提供了条件。
通过 master的 info replicaton
中的 master_replid2
可查看到。如果尚未发生过易主,则该值为 40 个 0。
# 4、无盘操作
Redis 6.0 对同步过程又进行了改进,提出了“无盘全量同步”与“无盘加载”策略,避免了耗时的 IO 操作。
● 无盘全量同步:master 的主进程 fork 出的子进程直接将内存中的数据发送给 slave,无需经过磁盘。
● 无盘加载:slave 在接收到master 发送来的数据后不需要将其写入到磁盘文件,而是直接写入到内存,这样 slave 就可快速完成数据恢复。
# 5、共享复制积压缓冲区
Redis 7.0 版本对复制积压缓冲区进行了改进,让各个 slave 的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有 slave的发送缓冲区,充分利用了复制积压缓冲区。