redis5.0.2源码分析——复制过程详解

一、建立主从关系

  • 在配置文件中加入slaveof {masterHost} {masterPort}启动生效
  • 在redis_server启动命令后加入--slaveof {masterHost} {masterPort}生效
  • 直接使用命令:slaveof {masterHost} {masterPort}生效

二、断开主从关系

从节点执行slaveof no one命令断开与主节点的复制关系。

三、切换主节点操作流程

1)断开与旧主节点复制关系。

2)与新主节点建立复制关系。

3)删除从节点当前所有数据。

4)对新主节点进行复制操作。

切主后从节点会清空之前所有的数据,线上人工操作时小心slaveof在错误的节点上执行或者指向错误的主节点。默认情况下,从节点使用slave-read-only=yes配置为只读模式。

四、主从结构拓扑

4.1 一主一从

用于主节点出现宕机时从节点提供故障转移支持。当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既保证数据安全性同时也避免了持久化对主节点的性能干扰。
注意主节点宕机后,先断开主从复制关系,再重启主节点。

4.2 一主多从

4.2.1 星形拓扑结构

一主多从结构(又称为星形拓扑结构)使得应用端可以利用多个从节点实现读写分离(见图6-5)。对于读占比较大的场景,可以把读命令发送到从节点来分担主节点压力
但是对于写并发量较高的场景多个从节点会导致主节点写命令的多次发送从而过度消耗网络带宽,同时也加重了主节点的负载影响服务稳定性。可下面采用树形结构

4.2.2 树形结构

当主节点需要挂载多个从节点时为了避免对主节点的性能干扰,可以采用树状主从结构降低主节点压力。

五、复制

5.1 简介

Redis 的复制功能分为同步( sync )和命令传播( command propagate )两个步骤:

  • 同步用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态。
  • 命令传播则用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态。

5.2 同步(从节点主动向主节点进行同步)

5.2.1 实现(全量复制和部分复制)

Redis 使用 psync 命令完成主从数据同步,同步过程分为:全量复制和部分复制

  • 全量复制:一般用于初次复制场景,它会把主节点全部数据一次性发送给从节点发送给从节点,当数据量较大时,会对主从节点和网络造成很大的开销。
  • 部分复制:用于处理在主从复制中因网络闪断等原因造成的数据丢失场景,当从节点再次连接上主节点后,如果条件允许,主节点会补发丢失数据给从节点。因为补发的数据远远小于全量数据,可以有效避免全量复制的过高开销

5.2.2 psync 命令的依赖

psync 命令运行需要以下组件支持:

  • 主从节点各自复制偏移量
  • 主节点复制积压缓冲区
  • 主节点运行 id

  参与复制的从节点都会维护自身复制偏移量。主节点在处理完写命令后,会把命令的字节长度做累加记录,统计在 info replication 中的 masterreploffset 指标中。 从节点在接收到主节点发送的命令后,也会累加记录自身的偏移量,并且会每秒钟上报自身的复制偏移量给主节点。 通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

5.2.3 复制偏移量

主从节点各自复制偏移量:

1、参与复制的主从节点都会维护自身的复制偏移量。
2、主节点在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在 info replication 中的 masterreploffset 指标中。
3、从节点每秒钟上报自身的的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量。
4、从节点在接收到主节点发送的命令后,也会累加自身的偏移量,统计信息在 info replication 中。
5、通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

5.2.4 复制积压缓冲区(repl_backlog)

  复制积压缓冲区是保存在主节点的一个固定长度的先进先出队列,默认大小为 1MB,当主节点有连接的从节点时。主节点响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区中

复制积压缓冲区大小有限,只能保存最近的复制数据,用于部分复制和复制命令丢失时的数据补救

主节点复制积压缓冲区的作用:

1、复制积压缓冲区是一个保存在主节点的一个固定长度的先进先出的队列。默认大小 1MB。
2、这个队列在 slave 连接时创建。这时主节点响应写命令时,不但会把命令发送给从节点,也会写入复制缓冲区。
3、他的作用就是用于部分复制和复制命令丢失的数据补救。通过 info replication 可以看到相关信息。

5.2.5 运行ID

  每个 Redis 节点启动后都会动态分配一个 40 位的十六进制字符串作为运行 ID。运行 ID 的主要作用是用来唯一标识 Redis 节点,比如说从节点保存主节点的运行 ID 来识别自己正在复制的时哪个主节点。

主节点运行 ID:

  • 每个 redis 启动的时候,都会生成一个 40 位的运行 ID。
  • 运行 ID 的主要作用是用来识别 Redis 节点。如果使用 ip+port 的方式,那么如果主节点重启修改了 RDB/AOF 数据,从节点再基于偏移量进行复制将是不安全的。所以,当运行 id 变化后,从节点将进行全量复制。也就是说,redis 重启后,默认从节点会进行全量复制

如果在重启时不改变运行 ID 呢?

1、可以通过 debug reload 命令重新加载 RDB 并保持运行 ID 不变。从而有效的避免不必要的全量复制。
2、他的缺点则是:debug reload 命令会阻塞当前 Redis 节点主线程,因此对于大数据量的主节点或者无法容忍阻塞的节点,需要谨慎使用。一般通过故障转移机制可以解决这个问题。

5.2.5 psync命令

5.2.5.1 psync命令使用

psync 命令的使用方式:

1 命令格式为:   psync{runId}{offset}
2 
3 runId:从节点所复制主节点的运行 id
4 offset:当前从节点已复制的数据偏移量

5.2.5.2 psync命令流程

psync 执行流程:

流程说明:从节点发送 psync 命令给主节点,runId 就是目标主节点的 ID,如果没有默认为 -1,offset 是从节点保存的复制偏移量,如果是第一次复制则为 -1.

5.2.5.2 psync命令回复

主节点会根据 runid 和 offset 决定返回结果:

  • 如果回复 +FULLRESYNC {runId} {offset} ,那么从节点将触发全量复制流程。
  • 如果回复 +CONTINUE,从节点将触发部分复制。
  • 如果回复 +ERR,说明主节点不支持 2.8 的 psync 命令,将使用 sync 执行全量复制。

5.3 全量同步

5.3.1 流程图

  全量复制是 Redis 最早支持的复制方式,也是主从第一次建立复制时必须经历的的阶段触发全量复制的命令是 sync 和 psync。之前说过,这两个命令的分水岭版本是 2.8,redis 2.8 之前使用 sync 只能执行全量不同,2.8 之后同时支持全量同步和部分同步

 

5.3.2 流程总结

slaveof 命令的执行

  • 1) 从节点发送 psync 命令进行数据同步,由于是第一次进行复制,从节点没有复制偏移量和主节点的运行ID,所以发送的命令时 PSYNC ? -1。
  • 2) 主节点根据 PSYNC ? -1 解析出当前为全量复制,回复 + FULLRESYNC 响应。
  • 3) 从节点接收主节点的响应数据保存运行 ID 和偏移量 offset。
  • 4) 主节点执行 bgsave 保存 RDB 文件到本地,有关 RDB 的知识可以查看https://www.cnblogs.com/remcarpediem/p/11644722.html
  • 5) 主节点发送 RDB 文件给从节点,从节点把接收的 RDB 文件保存在本地并直接作为从节点的数据文件,接收完 RDB 后从节点打印相关日志,可以在日志中查看主节点发送的数据量。

  需要注意,对于数据量较大的主节点,比如生成的 RDB 文件超过 6GB 以上时要格外小心。如果传输 RDB 的时间超过 repl-timeout 所配置的值,从节点将发起接收 RDB 文件并清理已经下载的临时文件,导致全量复制失败。

  • 6) 对于主节点开始保存 RDB 快照到从节点接收完成期间,主节点仍然响应读命令,因此主节点会把这期间写命令保存在复制缓存区(主从复制客户端的输出缓存区,当从节点加载完 RDB 文件后,主节点再把缓冲区内的数据发送给从节点,保证主从之间数据一致性。

  如果主节点创建和传输 RDB 的时间过长,可能会出现主节点复制缓存区(客户端输出缓冲区内)溢出。默认配置为 client-output-buffer-limit replica 256MB 64MB 60,如果60s内缓冲区消耗持续大于64MB或者直接超过256MB时,主节点将直接关闭复制客户端连接,造成全量同步失败。

  • 7) 从节点接收完主节点传送来的全部数据后会清空自身旧数据,该步骤对应如下日志。
  • 8) 从节点清空数据后开始加载 RDB 文件,对于加大的 RDB 文件,这一步操作依然比较耗时,可以通过计算日志之间的时间差来判断加载 RDB 的总耗时。
  • 9) 收到 SYNC 命令的主服务器执行 BGSAVE 命令,在后台生成一个 RDB 文件,并使用一个缓冲区记录从现在开始执行的所有写命令
  • 10) 当主服务器的 BGSAVE 命令执行完毕时,主服务器会将 GBSAVE 命令生成的 RDB 文件发送给从服务器,从服务器接收并载入这个 RDB 文件,将自己的数据库状态更新至主服务器执行 BGSAVE 命令时的数据库状态。
  • 11) 主服务器将记录在缓冲区里边的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。

5.3.3 时间开销

通过分析全量复制的所有流程,读者会发现全量复制是一个非常耗时费力的操作。它时间开销主要包括:

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

5.3.4 全量同步的缺点

  全量同步过程中不仅会消耗大量时间,还会进行多次持久化相关操作和网络数据传输,这期间会大量消耗主从节点所在服务器的 CPU、内存和网络资源。所以,除了第一次复制是采用全量同步无法避免,其他场景应该规避全量复制,采取部分同步功能。

注意:

1、如过 RDB 文件大于 6GB,并且是千兆网卡,Redis 的默认超时机制(60 秒),会导致全量复制失败。可以通过调大 repl-timeout 参数来解决此问题。
2、Redis 虽然支持无盘复制,即直接通过网络发送给从节点,但功能不是很完善,生产环境慎用。

5.4 部分同步

5.4.1 部分同步流程图

  部分复制主要是 Redis 针对全量复制的过高开销做出的一种优化措施,使用 psync {runId} {offset} 命令实现。当从节点正在复制主节点时,如果出现网络闪断或者命令丢失等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区存在这部分数据则直接发送给从节点,这样就保证了主从节点复制的一致性。补发的这部分数据一般远远小于全量数据,所以开销很小。

5.4.2 部分同步流程总结

执行过程分析:

  • 1 ) 当主从节点之间网络出现中断时,如果超过了 repl-timeout 时间,主节点会认为从节点故障并中断复制连接。
  • 2) 主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内部存在复制积压缓冲区( repl-backlog-buffer ),依然可以保存最近一段时间的写命令数据,默认最大缓存 1MB。

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

  • 4) 当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们作为 psync 参数发送给主节点,要求进行补发复制操作。

  • 5) 主节点接到 psync 命令后首先核对参数 runId 是否与自身一致,如果一致,说明之前复制的是当前主节点;之后根据参数 offset 在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发送 +CONTINUE 响应,表示可以进行部分复制。

  • 6) 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。

六、心跳检测

repl-disable-tcp-node-lay参数

  • 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机架或同机房部署。 默认关闭
  • 当开启时,主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂或带宽紧张的场景,如跨机房部署。

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

主从心跳判断机制如下所示:

  • 中从都有心跳检测机制,各自模拟成对方的客户端进行通信,通过 client list 命令查看复制相关客户端信息,主节点的连接状态为 flags = M,从节点的连接状态是 flags = S。
  • 主节点默认每隔 10 秒对从节点发送 ping 命令,可修改配置 repl-ping-slave-period 控制发送频率。
  • 从节点在主线程每隔一秒发送 replconf ack{offset} 命令,给主节点上报自身当前的复制偏移量。
  • 主节点收到 replconf 信息后,判断从节点超时时间,如果超过 repl-timeout 60 秒,则判断节点下线。

  replconf 命令不仅能实时监测主从节点网络状态,还能上报从节点复制偏移量。主节点会根据从节点上传的偏移量检查复制数据是否丢失,如果从节点数据丢失,再从主节点的复制缓存区中拉取丢失的数据发送给该从节点。

  注意:为了降低主从延迟,一般把 redis 主从节点部署在相同的机房/同城机房,避免网络延迟带来的网络分区造成的心跳中断等情况

七、异步复制和命令传播

7.1 异步复制

  主节点不但负责数据读写,还负责把写命令同步给从节点。写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成

异步复制的步骤很简单,如下:

  • 主节点接受并处理命令。
  • 主节点处理完后返回响应结果。
  • 对于修改命令,异步发送给从节点,从节点在主线程中执行复制的命令。

图片表示:

7.2 命令传播

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

八、参考文章

https://blog.csdn.net/QiuHaoqian/article/details/110938329

https://www.cnblogs.com/remcarpediem/p/11701234.html

posted @ 2021-07-12 23:18  Mr-xxx  阅读(111)  评论(0编辑  收藏  举报