Redis源码剖析——主从复制(1)

本文详细介绍了Redis主从复制的原理与流程,包括同步原理、复制ID和复制偏移量、部分同步与全量同步的判断以及执行流程。此外,还探讨了主从复制中的相关问题,如从机只读、过期key处理、min-replica机制等,旨在帮助读者深入理解Redis的主从复制机制。
摘要由CSDN通过智能技术生成

相关文章

Redis源码剖析——主从复制(2)
Redis源码剖析——主从复制(3)
Redis源码剖析——主从复制(4)
Redis源码剖析——主从复制(5)—共享复制缓冲区的方案

目录

前言

一、同步原理

1.master节点存储维护

2.slave节点存储维护

3.部分同步与全量同步的使用情况

4.命令传播

二、Replication执行流程

1.准备阶段

2.同步阶段

2.1 确定同步方式

2.2 全量同步流程

2.3 部分同步流程

三、Replication相关其他问题

1.从机只读

2.过期key处理

3.min-replica机制

4.主机关闭持久化时复制的安全性

5.master节点发送ping指令频度低,网络存在丢包

6.网络信息不同步,数据发送有延迟


前言

主要介绍主从复制的一些原理,相关代码分析可见同栏后续文章

一、同步原理

1.master节点存储维护

在主从复制模式下,Redis使用一对Replicaion ID, offset来唯一识别Master节点数据集的版本,要理解这个“版本“的概念需要认识Redis的以下三个概念:

  • Replication ID(复制ID):每个Redis的主节点都用一个随机生成的字符串来表示在其成为master角色的那一刻其内部存储数据的状态,由源码可知在第一个从节点加入时,Redis初始化了复制ID。
  • offset(复制偏移量):主从模式下,主节点会持续不断的向从节点传播引起数据集更改的命令,offset所表示的是主节点向从节点传递命令字节总数。它不是孤立存在的,需要配合复制积压缓冲区才能工作。
  • backlog(复制积压缓冲区):它是一个环形缓冲区,用来存储主节点向从节点传递的命令,它的大小是固定的,可存储的命令有限,超出部分将会被删除。它即可用于部分同步,也可用于命令传播阶段的命令重推。

下图直观看下它们之间的关系。

图示说明:

(1)图示Redis角色为Master,其复制ID(replid)为xxxx,当前的复制偏移量(offset)为1010;

(2)它有一个复制积压缓冲区(backlog),容量(backlog_size)为100,backlog起点相对于offset的偏移量(backlog_off)为1000,当前backlog存储的命令字节数(backlog_hislen)为11个,对应了backlog中[1000,1010]偏移量范围内的字节;

(3)offset始终与backlog中最后一个字节的偏移量相同。

2.slave节点存储维护

  • 复制ID:如果slave从未与任何master进行过主从复制,那就不存在;如果slave与master连接正常,复制ID存储在server.master内;如果slave与master连接断开,Redis会把它存储在server.cache_master
  • 复制偏移量:与复制ID一致。

server.master代表的是正在进行主从复制且正常工作的主节点信息;server.cache_master是曾经正常进行主从复制工作的主节点信息,它是为部分同步做准备的。

3.部分同步与全量同步的使用情况

  • 从节点与主节点建立连接后,需要从节点使用命令PSYNC <replid> <offset>向主节点发起同步请求,从节点会从server.cache_master中取出两个参数构建命令,然后发给主节点。注意:如果该从节点是全新的,从未与任何主节点进行主从复制,那么会使用特殊的命令:PSYNC ? -1。
  • 主节点接收到命令,解析请求中的复制ID和offset,然后去判断当前请求是否可以使用部分同步。
  •  能够使用部分同步需要满足以下两个条件(这里先不考虑主从切换导致的多复制ID情况):
  •        (1)复制ID与主节点的复制ID一致;
  •        (2)复制偏移量offset必须在backlog_off和offset的范围之间;
  •  不能使用部分同步,就不得不使用全量同步了。

Redis-1:replid和offset为默认值,说明它从未与主节点进行过同步操作,所以是进行全量同步;

Redis-2:replid主从节点一致,slave_offset>=backlog_off并且slave_offset<offset,说明该从节点丢失的数据可以通过复制积压缓冲区找回,所以可以进行部分同步;

Redis-3:replid主从节点一致,slave_offset<backlog_off,说明该节点丢失的数据过多,通过复制积压缓冲区无法找回,所以是进行全量同步;

Redis-4:replid主从节点一致,之前不是与当前节点进行主从复制,所以是进行全量同步;

总结一下:部分同步其实是以全量同步为基础(得到复制ID),用复制积压缓冲区中的缓存命令做命令的增量同步逻辑,不过受制于复制积压缓冲区的容量,它可容忍的范围是有限的。这与持久化机制的AOF混合持久化如出一辙,也与mysql中主从复制的Binlog思路不谋而合。

4.命令传播

当完成同步操作之后,master-slave便会进入命令传播阶段,此时master-slave的数据是一致的。

当maste执行完新的写命令后,会通过传播程序把该命令追加至复制积压缓冲区,然后异步地发送给slave。slave接收命令并执行,同时更新slave维护的复制偏移量offset。命令传播如下图所示:

如果slave可以收到每条传播指令,并执行成功,便可以保持与master的数据一致状态。但是master并不等待slave节点的返回,master与slave是通过网络通信,由于网络抖动等因素,命令传播过程不保证slave真正接收到,那如何在传播阶段确保主从数据一致呢?

在命令传播阶段,每隔一秒,slave节点向master节点发送一次心跳信息,命令格式为REPLCONF ACK <offset>

命令中的offset是就是slave最新的复制偏移量,master接收后便会与自己的offset对比。如果从节点数据缺失,主节点会推送缺失的数据(这句话写的很虚,我在源码中没有找到相关逻辑,但是参考的所有文章都提到了这个点。如果哪位同学了解,烦请告知)

二、Replication执行流程

主从复制的流程分为三个阶段:准备阶段、同步阶段、命令传播阶段,先上图再依次解释。

1.准备阶段

准备阶段完成与master的连接,主从之间通过命令一问一答处理状态检查及身份认证工作,为接下来的数据同步打好基础。

  • 设置master节点host和port:这一步很好理解,当接收到slaveof命令后,Redis会设置master节点的host和port,同时设置服务的复制状态为REPL_STATE_CONNECT
  • 与master建立网络连接:与master的网络连接是在周期性函数serverCron中触发的,如果发现服务状态为REPL_STATE_CONNECT,会调用函数connectWithMaster创建与master的网络连接。连接成功后,修改复制状态为REPL_STATE_CONNECTING,同时触发回调函数syncWithMaster开始与master节点的命令交互。连接失败,则等待下次重试。
  • 发送PING命令:与master连接创建成功后,slave向master发送PING命令。通过PING命令检查网络连接的读写状态是否正常,还可以检查master能否正常处理命令请求。如果slave读取PING命令回复超时或者master返回错误信息,将断开并重连服务器。服务器返回PONG,则继续执行下一个步骤。
  • 身份认证:检查从服务器是否设置masterauth,设置则进行身份验证,未设置则跳过该步骤。身份认证失败,则断开重连。
  • 发送slave端口信息:slave向主服务器发送自己的监听端口号,master收到之后会将端口号记录到slave对应的状态属性中,可通过命令client list查看。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import  ssl
ssl._create_default_https_context = ssl._create_unverified_context

2.同步阶段

2.1 确定同步方式

该阶段首先要做的就是master-slave共同协作确定同步的方式,简单来说就是两步:slave发送PSYNC命令、master判断决定同步方式进行回复。

slave发送PSYNC指令。 PSYNC命令要为参数replid和offset赋值,这里需要区分两种情况:

  • 如果slave是第一次与当前master进行主从复制,则使用默认值:replid=?; offset=-1
  • 如果slave之前与该master进行过全量复制,则根据缓存的master信息、复制偏移量进行赋值;

怎么判断slave是第一次进行主从复制呢?
Redis核心结构server的cached_master保存了master节点的信息,只有进行过主从复制才会赋值,否则为空。

master判断决定同步方式。 master接收命令后由syncCommand函数处理,然后调用masterTryPartialResynchronization函数判断同步的方式,从代码分析可知,有以下几种情况:

  • offset解析失败,执行全量同步;
  • replid与master的复制ID不一致,执行全量同步;
  • offset不在master的backlog缓冲区范围内,执行全量同步;
  • 其余满足部分同步条件,执行部分同步;

 接下来,从master和slave两个角度详细看下全量同步和部分同步的执行流程。

2.2 全量同步流程

先看master。 确认需要执行全量同步后,master直接进入处理流程。这里提一下,全量同步过程中Redis是依靠slave的状态来驱动整个流程的,我先通过一张图描述下全量同步过程及slave的状态流转,再做说明:

  • 当主节点收到从节点发来”PSYNC”命令,并且需要完全重同步时,将从节点的状态置为REDIS_REPL_WAIT_BGSAVE_START,表示该从节点等待主节点后台RDB数据转储的开始;
  • 接下来,当主节点开始在后台进行RDB数据转储时,将从节点的状态置为REDIS_REPL_WAIT_BGSAVE_END,表示该从节点等待主节点后台RDB数据转储的完成;
  • 如果主节点在进行后台RDB数据转储时,使用的是有硬盘复制的方式,则RDB数据转储完成时,将从节点的状态置为REDIS_REPL_SEND_BULK,表示接下来要将本地的RDB文件发送给客户端了;当所有的RDB数据发送完成后,将从节点的状态置为REDIS_REPL_ONLINE,表示可以向从节点发送累积的命令流了。

在看slave侧。 在准备阶段,slave发送psync指令后,就等待master的回复,当收到全量同步的回复后,开始执行全量同步流程。过程如下:

  • 如果slave还有级联的slave,则断开所有与它们的网络链接,并清空复制积压缓冲区;
  • 创建rdb临时文件,接收master传输的文件流并写入;
  • 停止正在进行的rdb持久化、aof持久化流程;
  • 重命名临时文件为正式的rdb文件,执行数据加载;
  • 基于当前与master的网络链接,创建slave的客户端,把master作为slave的客户端;
  • 设置slave的复制id,创建复制积压缓冲区;
  • 进入命令传播阶段;

2.3 部分同步流程

对于全量同步,部分同步要简单的多。

master判定可以使用部分同步方式,执行以下流程:

  • 修改slave状态为SLAVE_STATE_ONLINE,并把slave加入从机列表;
  • 向slave回复部分同步命令,"+CONTINUE replid";
  • 按照slave请求的offset,从复制积压缓冲区提取命令发送至slave;

slave接收到部分同步的回复后,执行以下流程:

  • 对比master复制ID是否发生改变,如果改变了,则更新复制ID,并把原来的复制ID转移至复制ID2;如果有级联的slave,需要断开连接,让他们重连;
  • 基于当前与master的连接,创建slave的客户端,准备接收命令。
  • 接收master传输的命令并执行;
  • 进入命令传播阶段。

三、Replication相关其他问题

1.从机只读

默认情况下,从机工作在只读模式下,即无法对从机执行写指令。如下图,对从机执行写指令将会返回错误。

如果slave也配置了自己的从服务器(sub-slave),那么sub-slave只会同步从master服务器同步到slave的数据,而不会同步我们直接写入slave服务器的数据。如:A--->B--->C,如果B关闭了只读模式,C只会同步来自A的命令且与A保持一致。

2.过期key处理

Redis可以通过设置key的过期时间来限制key的生存时间,Redis处理key过期有惰性删除和定期删除两种机制,这一机制依赖Redis实例的计时能力。如果主机、从机同时启用key过期的处理机制,可能会导致一些问题。为此,Redis采取了三个技术手段来解决key过期的问题:

  • 从机禁用主动key过期机制。主机在执行key过期后,会以DEL指令的方式向所有从机传播指令,从而保证从机移除过期的key。
  • 依赖主机的key过期机制是无法做到实时性的,所以针对读操作,从机将会按照自己的时钟向客户端返回key不存在。
  • 为防止Lua脚本执行期间key过期,Lua脚本将会传播给从机执行。

3.min-replica机制

Redis主从复制不仅仅是解决主机、从机之间数据同步的问题,它还需要保证数据的安全性。这里的安全性主要是指主从之间数据同步达到一致的效率,以及主从结构下读写分离场景中分布式系统的可靠性。

Redis采用异步复制机制,它无法真正保证每个从机都能准确的收到传播的指令,所以主从之间必然会存在命令丢失的时间窗口。

为此,Redis引入了min-replicas选项,该机制在redis.conf中有两个配置项:

  • min-replicas-to-write <number of replicas>:至少有N个从机才能写入数据。保证从机最低数量。
  • min-replicas-max-lag <number of seconds>:如果每个从机的延迟值大于N,则拒绝写入数据。保证主从同步延迟。

这一机制是通过从机与主机之间心跳来实现的,如上文所讲,从机每隔一秒向主机发送一次心跳数据,基于心跳,主节点可以:

  • 更新从机同步确认时间:基于主节点时间及同步确认时间计算延迟值。
  • 更新主从最后通信时间:用于从机通信超时检测,如果通信超时,主机会移除从机。

4.主机关闭持久化时复制的安全性

当master关闭了持久化时,如果发生故障后自动重启时,由本地没有保存持久化的数据,重启的Redis内存数据为空,而slave会自动同步master的数据,就会导致slave的数据也会被清空。

所以,我们应该尽可能为master节点开启持久化,这样可以防止Redis故障重启时数据丢失,进而导致slave数据被清除。如果确实无法开启持久化机制,那应该配置master节点无法自动重启,确保从机可以成为新的master节点,防止数据被清除。

5.master节点发送ping指令频度低,网络存在丢包

master节点默认10s向slave节点发送一次ping指令,因为master节点不仅要处理大量的写任务,还可能维护着多个master,所以ping设置的不太及时。但是当ping指令在网络中存在丢包时,master节点如果设置的超时时间太短,就会导致master节点与slave节点断开连接。

解决方案有:提高master节点ping的频度,超时时间repl-time设置为ping指令时间的5~10倍。

6.网络信息不同步,数据发送有延迟

当主从同步中网络数据发送有延迟的时候,就会造成多个slave获取到的数据不同步,解决方案是优化master节点和slave节点的网络环境,通常是放置在一个机房部署。另外要监控master和slave节点的延迟,如果延迟过大,可以暂时屏蔽对slave节点的访问。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值