redis主从复制图解
明确几个概念
复制偏移量
主从节点都维护这一个复制偏移量(offset)
,它代表着当前节点接受数据的字节数,主节点表示接收客户端的字节数,从节点表示接收主节点的字节数,比如从节点接收主节点传来的 N 个字节数据时,从节点的offset
会增加 N。
偏移量的作用非常大,它是用来衡量主从节点数据是否一直的唯一标准,如果主从节点的 offset
相等,表明数据一直,否则表明数据不一致。在不一致的情况下,可以根据两个节点的 offset
找出从节点的缺少的那部分数据。比如,主节点的 offset
是 500,从节点的 offset
是 400,那么主节点在进行数据传输时只需要将 401 ~ 500 传递给从节点即可,这就是部分复制
。
复制积压缓冲区
复制积压缓冲区是一个由主节点维护的缓存队列,它具有如下几个特点:
- 由主节点维护
- 固定大小,默认为 1MB,配置参数为:
repl-backlog-size
- 是一个先进先出的队列
在命令传播节点,主节点除了将写命令传递给从节点,也会将写命令写入到复制积压缓冲区中
,当做一个备份,用于在部分复制流程中。由于它是先进先出的队列,且大小固定,所以他只能保存主节点最近执行的写命令,当主从节点的 offset
相差较大时,超出了复制积压缓冲区的范围,则无法进行部分复制,只能进行全量复制了,所以为了能够提高网络中断引起的全量复制,我们需要认真评估复制积压缓冲区的大小,将其适当调大,比如网络中断时间是 60s,主节点每秒接收的写命令为
100KB,则复制积压缓冲区的平均大小应该为 6MB,所以我们可以将其大小设置为
6MB,甚至是
10MB`,来保证绝大多数中断情况下都可以使用部分复制。
运行 ID(runid)
每个 Redis 节点在启动时都会生成一个运行 ID,即 runid
,该 ID 用于唯一标识 Redis 节点,它是一个由 40 位随机的十六进制的字符组成的字符串,通过 info server
命令可以查看节点的 runid
从节点在初次建立连接进行全量复制时(从节点发送 psync ? -1
),主节点会将自己的 runid
告知给从节点,从节点将其保存起来。当主从节点断开重连时,从节点会将这个runid
发送给主节点,主节点会根据从节点发送的 runid
来判断选择何种复制:
- 如果从节点发送的
runid
与当前主节点的runid
一致时,主节点则尝试进行部分复制,当然能不能进行部分复制还要看偏移量是否在复制积压缓冲区 - 如果从节点发送的
runid
与当前主节点的runid
不一致时,则进行全量复制
全量复制过程
-
由于是第一次进行数据同步,从节点并不知道主节点的
runid
,所以发送psync ? -1
-
主节点接收从节点的命令后,判定是进行全量复制,所以回复
+FULLRESYNC
,同时也会将自身的 runid 和 偏移量发送给从节点,响应为 +FULLRESYNC {runid} {offset} -
从节点接受主节点的响应后,会保存主节点的 runid 和 偏移量 offset。
-
主节点响应从节点命令后,会执行
bgsave
,将生成的 RDB 文件保存在本地。主节点接受从节点的部分请求,但是runid
不一致,进行全量复制,返回+FULLRESYNC
,并将自身的runid
和offset
返回给从节点。 -
主节点将生成的 RDB 文件发送给从节点,从节点接收后保存在本地直接将其作为数据文件,如果从节点本地有 RDB 文件,则从节点会先清空 RDB 文件。
-
如果从节点还开启了
AOF
,则还会进行AOF
重写
部分复制
当主从节点在命令传播节点发生了网络中断,出现数据丢失情况,则从节点会向主节点请求发送丢失的数据,如果请求的偏移量在复制积压缓冲区中,则主节点就将剩余的数据补发给从节点,保持主从节点数据一致,由于补发的数据一般都会比较小,所以开销相当于全量复制而言也会很小,流程如下:
-
当主从节点出现网络闪退时,如果超过了
repl-timeout
时间,主节点会认为从节点出现故障不可达 -
由于主节点没有宕机,所以他依然会响应客户端命令,当然这些命令也不会丢失,都会存储在复制积压缓冲区中,默认
1MB
-
当主从直接回复连接,从节点再次连接主节点
-
当主从建立连接后,由于从节点保存了主节点的
runid
和offset
,所以只需要发送命令psync {runid} {offset}
即可 -
主节点接受从节点的
psync
命令,会先核对请求的runid
是否和自身的的runid
一致,如果一致,说明该从节点复制的当前主节点。然后查看请求的offset
是否在复制积压缓冲区,如果在则进行部分复制,否则进行全量复制,部分复制回复+CONTINUE
响应,从节点接受回复 -
在进行部分复制时,主节点只需要根据 offset 将复制积压缓冲区的数据补发给从节点即可
补充:psync 命令的执行过程
- 首先从节点根据当前状态来决定如何调用
psync
命令
- 如果主从节点从未建立过连接或者之间执行过
slave of none
,则从节点发送命令 psync ? -1,向主节点请求全量复制。 - 如果主从节点建立过连接,则发送命令
psync {runid} {offset}
尝试部分复制,具体是全量还是部分复制,则需要根据主节点的情况来确定
- 主节点则根据自身情况来做出不同的响应:
- 如果主节点的版本低于 2.8 ,则响应
-ERR
,从节点接受该回复后发送sync
进行全量复制 - 如果主节点发现请求的命令为
psync ? -1
, 则判定该从节点是第一次进行连接,则响应+FULLRESYNC <runid> <offset>
,进行全量复制 - 如果主节点比对命令请求的
runid
和自身的runid
不一致或者一致,但是请求的 offset 不在复制积压缓冲区中,则响应+FULLRESYNC <runid>
进行全量复制 - 如果主节点比对命令请求的
runid
和自身的runid
一致,且offset
也在复制积压缓冲区,则响应+CONTINUE
进行部分复制