Redis的复制过程

连接和配置

Redis实例分为主节点和从节点,默认都是主节点,每个从节点只能有一个主节点,一个主节点可以有多个从节点。

使用slaveof命令配置复制,可以提前写到配置文件中,也可以运行期动态配置。

配置复制的方式:

  • 配置文件中加入slaveof {masterHost} {masterPort}, 然后启动生效

  • redis-server启动命令后加入 –slaveof {masterHost} {masterPort}生效

  • 使用命令slaveof {masterHost} {masterPort}生效

slaveof是异步命令,执行slaveof命令时,节点只保存主节点信息然后返回,后续复制流程在节点内部异步进行。

通过命令slaveof no one来断开与主节点的复制关系。断开后从节点晋升为主节点。

slaveof命令还能用来进行切主操作,执行slaveof {newMaterIP} {newMasterPort}命令即可,切主会断开与旧主节点复制关系,与新主节点建立复制关系,删除从节点当前的所有数据,然后对新主节点进行复制操作。

对于需要密码验证的主节点,需要配置从节点的masterauth参数与主节点密码保持一致。

默认从节点为只读模式,建议不要修改。

参数reps-disable-tcp-nodelay􏰊􏾠􏰆􏾢􏲅􏰨􏰠用于配置是否关闭TCP_NODELAY,默认关闭:

  • 关闭时,主节点的命令数据无论大小都会及时发送给从节点,这样延迟较小,但增加了网络带宽消耗。

  • 开启时,主节点会合并较小的TCP数据包从而节省带宽,默认发送间隔取决于Linux的内核,一般默认40毫秒,但这回增大主从之间的延迟。

拓扑结构

Redis复制拓扑结构分为三种:

一主一从

一主多从

树状主从:树状主从使得从节点不但可以复制主节点的数据,同时可以作为其他从节点的主节点继续向下层复制。

复制过程

  1. 保存主节点信息

    执行slaveof后从节点只保存主节点的地址信息后便返回,这时建立复制流程还没开始

  2. 从节点内部每秒运行的定时任务维护复制相关逻辑,当定时任务发现有新的主节点后,会尝试建立网络连接

    从节点会建立一个socket, 专门用于接受主节点发送的复制命令。如果无法建立连接,定时任务会无限重试知道连接成功或者等到执行slaveof no one取消复制

  3. 连接建立成功后从节点发送ping请求检测套接字是否可用以及主节点当前是否能接受处理命令

    如果发送ping命令后,从节点没收到主节点的pong回复或者超时,从节点会断开连接,下次定时任务会发起重连

  4. 权限验证,如果设置了密码验证的话会需要从节点配置的密码与主节点相同才能通过验证

  5. 同步数据集,采用psync命令进行数据同步,分为全量同步和部分同步
  6. 命令持续复制,当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下下主节点会持续把写命令发送给从节点,保证主从数据一致性,写命令的发送过程是异步的,并不等待从节点复制完成。

心跳机制

主从节点建立复制后,会维护着长连接并彼此发送心跳命令,主从双方都有心跳检测机制

  • 主节点默认每10秒对从节点发送ping命令,判断从节点的存活性和连接状态

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

    主节点根据replconf命令判断延迟时间,正常延迟应该在0-1秒之间,如果超过配置的repl-timeout值,则判定从节点下线并断开连接。

数据同步

同步过程分为全量复制和部分复制,使用psync命令, psync命令的格式是:psync {runid} {offset}

runid是主节点运行ID,offset是复制偏移量。

参与复制的主从节点都会维护自身复制偏移量,主节点在处理完命令后,会把命令的字节长度做累加记录,从节点每秒上报自身的复制偏移量给主节点,因此主节点也会保持从节点的复制偏移量。

从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量。

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

部分复制用于处理主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连接上主节点时,如果丢失的数据存在于主节点的复制缓冲中,则会补发丢失数据,如果复制缓冲中的数据已被新数据覆盖,则需要再进行全量复制,因此需要把复制缓冲配置到合理的范围。

全量复制

第一次建立复制时进行全量复制,流程如下

  1. 发送psync命令进行数据同步,第一次进行复制没有复制偏移量和主节点的运行ID,发送psync ? -1

  2. 主节点根据复制偏移量是-1解析出是全量复制,回复+FULLRESYNC响应,内容包括主节点运行ID和复制偏移量

  3. 从节点接收主节点响应数据并保存主节点运行ID和复制偏移量

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

  5. 主节点发送rdb文件给从节点,从节点接收rdb文件并保存在本地作为数据文件

    注意对于rdb文件较大的情况,传输文件会非常耗时,速度取决于主从节点之间的网络带宽,如果rdb从创建到传输文件消耗的总时间超过了repl-timeout配置的值,从节点将放弃接收rdb文件并清理已经下载的临时文件,导致全量复制失败

    对于数据量较大的节点,建议调大repl-timeout参数,对于千兆网卡的机器,理论峰值每秒传输100MB, 6GB的 rdb至少需要60秒,默认60秒的超时时间下,很容易发生超时。

    Redis支持无盘复制,生成的rdb文件不保存到硬盘而是直接发送给从节点,可以通过参数配置这一功能,不过该功能尚在试验阶段

  6. rdb文件传输到接收完成阶段,主节点仍在响应请求,并把写命令保存在复制缓冲区内,从节点加载完rdb文件后,主节点会再把缓冲区内的数据发送给从节点。复制缓冲区是一个环形数组,如果主节点创建和传输rdb的时间过长,导致未同步的数据被后续的数据覆盖掉了,主节点将直接关闭复制连接,造成全量同步失败。

    因此需要根据主节点数据量和写命令的并发量调整缓冲区大小的配置,避免全量复制期间缓冲区被覆盖。

  7. 主节点发送完所有的数据就可认为全量复制完成。从节点接收完全部数据后会情况自身旧数据,然后加载rdb文件

  8. 从节点成功加载rdb文件后,如果开启了AOF持久化功能,会立刻做bgrewriteaof操作,为保证全量复制后AOF文件立刻可用

全量复制是一个非常耗时的操作,主要的时间开销包括:

  • 主节点bgsave时间
  • rdb文件网络传输时间
  • 从节点清空数据时间
  • 从节点加载rdb时间
  • 可能的AOF重写时间

线上6G左右的主节点,从节点发起全量复制的总耗时大概在2分钟左右,所以除了第一次复制采用全量复制外,其他情况下应尽量避免。

部分复制

当从节点正在复制主节点时,发生网络闪断等异常情况时,从节点会要求补发丢失的命令数据,如果主节点的复制缓冲区内存在这部分数据,则直接发送给从节点。

流程如下:

  1. 发生网络中断时,如果超过repl-timeout时间,主节点会关闭复制连接
  2. 中断期间,写命令会保存在主节点的复制缓冲区,缓冲区默认1MB
  3. 网络恢复后,从节点发送psync {runid} {offset}, 如果runid与主节点一致,并且offset后的数据在主节点的复制缓冲区,则向从节点发送偏移量之后的数据

核心总结

  • 使用slaveof命令配置复制,可以写在配置文件里也可以直接在运行期动态执行
  • 数据同步分为全量复制和部分复制,使用psync {runid} {offset}命令,主节点和从节点都保存有自己复制偏移量,第一次进行全量复制时offset为-1
  • 全量复制会在主节点生成rdb文件,然后发送给从节点。另外主节点的写命令都会暂存在一个环形数组的复制缓冲区,依靠它和复制偏移量来进行部分复制