Redis复制过程

200 阅读4分钟

Redis复制过程

原文链接:mp.weixin.qq.com/s/0VVYTyAI1…

Redis的复制过程分为三个阶段:

  1. 复制初始化:从节点连接主节点并发送自己的监听端口
  2. 数据同步:将主节点数据同步给从节点
  3. 命令传播:主节点持续将写命令发送给从节点,主从保持心跳和维护复制偏移量

复制初始化

当执行slaveof命令的时候,从节点向主节点发送ping命令,以确认主节点是否可用,主节点返回pong代表可用,如果超时未返回,从节点会断开socket连接,然后进行重试。

如果主节点设置了密码,则从节点需要设置masterauth参数,此时从节点会发送auth命令。

身份验证完成后,从节点发送自己的监听端口,主节点保存其端口信息。

数据同步

从节点向主节点发送psync命令同步主节点数据,分为全量复制和增量复制。

全量复制:一般用于初次复制场景,会把主节点全部数据一次性发送给从节点,当数据量较大时,会对主从节点和网络造成很大开销。

增量复制:用于处理在主从复制中因网络断开后又重连的场景,当从节点再次连接上主节点后,如果条件允许,主节点会补发丢失数据给从节点。补发的数据量小于全量数据,可有效避免全量复制的高开销。

从redis2.6到4.0,复制命令有所变化:

  • redis版本<=2.6时,复制采用sync命令,无论是第一次主从复制还是断线重连都采用全量复制;
  • 2.8<=redis版本<4.0,复制采用psync,redis在断线重连时可以使用增量复制;
  • redis版本>=4.0,也采用psync,优化了增量复制;

psync命令格式:psync {offset} {runId}。runId是redis运行的唯一id。每个redis实例在启动的时候都会随机生成一个长度为40的唯一字符串来标识当前redis节点。

全量复制

1)从节点发送psync命令进行数据同步,由于是第一次复制,从节点没有复制偏移量和主节点的运行ID,所以发送的命令是PSYNC ? -1。

2)主节点根据PSYNC ? -1解析出当前为全量复制,回复+ FULLRESYNC响应。

3)从节点接收主节点的响应数据保存runId和偏移量offset。

4)主节点执行bgsave保存rdb文件到本地。

5)主节点发送rdb给从节点,从节点把接收的rdb文件保存在本地,清空自身旧数据后将其加载到内存。

当rdb文件过大,传输时间超过repl-timeout所配置的值时,会导致复制超时,连接关闭。

当rdb文件过大,从节点加载也会比较耗时。

6)主节点把保存rdb到从节点接收完成期间的的写命令保存在复制挤压缓冲区内,当从节点加载完rdb文件后,主节点再把缓冲区内的数据发给从节点。

如果传输rdb的时间过长,可能会出现客户端缓冲区溢出。

默认配置为client-output-buffer-limit slave 256MB 64MB 60,如果缓冲区大于256MB或60s内缓冲区持续大于64MB时,主节点将直接关闭复制客户端连接,造成全量同步失败。

7)从节点执行主节点发来的写命令。

增量复制

增量复制通过PSYNC {runId} {offset}实现。从节点重连主节点后,要求主节点补发丢失的命令数据,如果主节点的复制挤压缓冲区内存在这部分数据则直接发给从节点。

1)当主从直接网络中断时,如果超过了repl-timeout时间,主节点会认为从节点故障并断开连接。

2)主从节点断开期间,主节点的复制积压缓冲区保存了最近一段的写命令数据,默认最大缓存1MB。

3)从节点网络恢复后,从节点会再次连接上主节点。

4)从节点之前保存了offset和主节点runId,因此会把它们作为PSYNC参数发送给主节点。

5)主节点核对runId后,根据offset在复制积压缓冲区查找,如果存在此部分数据则对从节点发送+CONTINUE响应,表示可以进行部分复制。

6)主节点根据偏移量把复制积压缓冲区内的数据发送给从节点。

命令传播

心跳检测

主从节点建立复制后,它们之间维护着长连接并彼此发送心跳命令。

主节点默认每隔10s向从节点发送PING命令,判断从节点的状态。

从节点在主线程中每隔1s发送replconf ack {offset}命令,给主节点上报自己当前的复制偏移量。

异步复制和命令传播

主节点向从节点发送写命令的过程是异步的,也就是说主节点处理完写命令后直接返回给客户端,并不等待从节点复制完成。

这个异步过程由命令传播程序来处理,它不仅会将写命令发送给所有从服务器,还会将写命令放入复制积压缓冲区里。