redis之主从复制

Redis主从复制确保数据冗余和高可用性,涉及完全重同步、部分重同步和命令传播。完全重同步通过BGSAVE生成RDB文件并发送给从服务器,部分重同步利用复制积压缓冲区减少数据丢失。命令传播保持主从状态一致,心跳检测和复制偏移量校验保证数据完整性。
摘要由CSDN通过智能技术生成

主从复制的概念

在Redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,我们称呼被复制的服务器为主服务器,而对主服务器进行复制的服务器为从服务器。
进行复制中的主从服务器双方的数据库将保存相同的数据,这种现象称为“数据库状态一致”,也成“一致”。

为什么需要主从复制

  • 读写分离
  • 数据容灾

复制功能的实现

Redis复制功能的实现是通过同步命令传播两个操作实现的。大致过程:当从服务器第一次或者断开重连后向主服务器发送同步命令后,会进行同步操作使主从服务器一致,但是这种一致并不是一成不变的,当主服务器执行了客户端的写命令,就可能导致主从服务器状态不一致;为了使主从服务器再次回到一致状态,主服务器需要对从服务器执行命令传播操作:主服务器会将自己执行的写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器的状态再次回到一致。对于同步命令传播的具体过程,下面会详细介绍。

需要注意的是由于同步操作,在数据量很大的时候非常的消耗资源和耗时,甚至会导致redis在一段时间的不可用,因此在redis2.8版本之后对同步操作进行了优化,将之前的同步操作分成了“完全重同步”和“部分重同步”,而Redis2.8之前的版本的同步操作只相当于Redis2.8版本之后的完全重同步的功能。当然,Redis2.8前后所执行同步命令也是不一样的,2.8之前使用SYNC,2.8之后使用PSYNC。因为新的同步操作包含了旧的的同步操作,因此下面我们以Redis2.8版本之后的新同步逻辑进行讲解。

同步过程详解

Redis2.8之后的版本的同步操作分为完全重同步部分重同步

在了解同步操作之前,先了解PSYNC命令的格式:PSYNC runID offset,其中runID是要上次复制的主服务器IDoffset是从服务器的复制偏移量。需要注意的是在从服务器进行第一次同步操作或者之前执行过SLAVEOF no one命令(也就是完全重同步)的时候,此时从服务器发送的同步命令格式是:PSYNC ?-1,因为此时并不知道主服务器的ID,也没有复制偏移;而在从服务器已经复制过某个主服务器后,发送命令的格式就是PSYNC runID offset,然后会根据runID和offset这两个参数来判断是执行完全重同步还是部分重同步
为了更好地解释同步的过程,我们先了解几个概念:

  • 服务器ID:每个redis服务器都会有自己的运行ID,在一个redis集群中这个ID必须是唯一的,是这台服务器的唯一标识

  • 复制偏移量:redis中的每个命令都会有自己的复制偏移量,执行复制的双方分别维护一个复制偏移量

    • 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
    • 从服务器每次收到主服务器的N个字节的数据时,就将自己的复制偏移量的值加上N
  • 复制积压缓冲区:由主服务器维护的一个固定长度的先进先出队列,默认大小为1MB,但是可以根据需要通过修改配置文件中的real-backlog-size的值来修改,这个值的大小设置非常关键,直接关系到部分重同步的命中的概率
    当主服务器进行命令传播时,它不仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区,也就是说主服务器的复制积压缓冲区里面会保存一部分最近传播的命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量。
    设置复制缓冲区大小有个参考公式:复制缓冲区大小=2*(second*write_size_per_second),其中:second是从服务器断线后重新连接上主服务器所需的平均时间(以秒计算),write_size_per_second是主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和)。

完全重同步

完全重同步的过程如下:

  • 从服务器向主服务器发送PSYNC命令
  • 收到PSYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
  • 当主服务器的BGSAVE命令执行完毕,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接受并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
  • 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态

当然在从服务器进行第一次复制时,主服务器会将自己的服务器ID发送给从服务器并进行保存,以便后续复制时使用

为什么说完全重同步操作非常消耗资源?
每次执行SYNC或PSYNC进行完全重同步时,主从服务器都会进行如下操作:

  • 主服务器需要执行BGSAVE命令来生成RDB文件,这个生成操作会耗费主服务器大量的CPU、内存和磁盘I/O资源
  • 主服务器需要将自己生成的RDB文件发送给从服务器,这个发送操作会耗费主服务器的大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响
  • 接收到RDB文件的从服务器需要载入发送来的RDB文件,并且在载入期间,从服务器会因为阻塞而无法处理命令请求

部分重同步

部分重同步发生在从服务器非第一次复制(例如断开重连)的时候,此时从服务器会发送PSYNC runID offset命令给主服务器,主服务器通过一系列判断后发现可以执行部分重同步就会将offset偏移量之后的数据发送给从服务器,从服务器接收并执行完这些数据后就会与主服务状态一致。

那么要满足什么条件主服务器才会执行部分重同步呢?
只有同时满足以下两个条件的情况下,主服务器才会执行部分重同步,否则执行全部重同步:

  • 从服务器发送的runID必须和当前的主服务器id一致
  • 从服务器发送的offset复制偏移量之后的数据(也就是offset+1开始的数据)必须在主服务器复制缓冲区

命令传播过程详解

命令传播的概念比较简单,当同步操作执行完后,主从服务器两者的数据库将达到一致状态,但是这种一致状态并不是一成不变的,当主服务执行客户端发送的命令的时,主服务器的数据库就有可能会被修改,并导致主从服务器状态不一致。

这个时候为了让主从服务器的状态再次回到一致,主服务器需要对从服务器执行命令传播操作:主服务器会将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器将再次回到一致状态。

PSYNC命令的实现

PSYNC命令的调用方法有两种:

  • 如果从服务器以前没有复制过任何主服务器,或者之前执行过SLAVEOF no one命令,那么从服务器在开始一次新的复制时向主服务器发送PSYNC ?-1命令,主动请求主服务器进行完整重同步(因为这时也不可能进行部分重同步)
  • 如果从服务器已经复制过某个服务器,那么从服务器在开始一次新的复制时向主服务器发送PSYNC 命令,由主服务器来判断对从服务器执行哪种同步操作

接受到PSYNC命令的主服务器向从服务器返回以下三种回复中的一种:

  • 如果主服务器返回+FULLRESYNC <offset回复,那么表示主服务器与从服务器执行完整重同步操作:其中runID是这个主服务器的运行ID,从服务器将这个ID保存起来,在下一次发送PSYNC命令时使用;而offset则是主服务器当前的复制偏移量,从服务器将这个值作为自己的初始化复制偏移量。
  • 如果主服务器返回+CONTINUE回复,那么表示主服务器将于从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了。
  • 如果主服务器返回-ERR回复,那么表示主服务器的版本低于redis2.8,它识别不了PSYNC命令,从服务器将向主服务器发送SYNC命令,并与主服务器执行完整同步操作。

其实介绍完上面的一些概念,主从复制的原理已经出来了,下面咱们再总结一下(第一次执行主从复制为例)。
客户端向从服务器发送slaveof命令

  • 从服务器向主服务器发送PSYNC ? -1命令,请求主服务器执行完整重同步操作
  • 主服务器在接收到完整重同步请求后,在后台重启一个线程执行BGSAVE命令,也就是对此时数据快照生成RDB文件,并向从服务器返回+FULLRESYNC 主服务器id、主服务器此时的偏移量等数据,而主线程仍然可以接受请求,并将请求放到缓冲区
  • 当主服务器的BGSAVE命令执行完毕时,主服务器将生成RDB文件发送给从服务器,从服务器完成此文件的接受和载入,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
  • 主服务器将记录在缓冲区中的所有写命令发送给从服务器,主服务器修改自己的复制偏移量,从服务器接收成功后,也会修改自己的复制偏移量,这样主从服务器的数据就保持一致了

对于如果不是第一次执行复制的情况,需要根据实际情况来判断是执行完全重同步还是部分重同步的操作,这里不在累述。

复制工作的全流程

  • 设置主服务器的地址和端口
    从服务器在接收到客户端发送的SLAVEOF命令(例如:slaveof 127.0.0.1 12345)后会将主服务器的ip和端口保存到服务器状态的masterhost属性和masteport属性里面。
    SLAVEOF命令是一个异步命令,在完成masterhost属性和masteport属性设置工作后,从服务器会向客户端返回OK,表示复制指令已经被接收,而实际的复制工作将在OK之后才真正开始执行。
  • 建立套接字
    从服务器根据主服务器的ip和端口创建连向主服务器的套接字连接。
    如果从服务器创建的套接字能成功连接到主服务器,那么从服务器将为这个套接字关联一个专门的事件处理器,这个处理器将负责后续的复制工作,比如接受RDB文件,以及接受主服务器的命令传播的写数据等。
    而主服务器在接受从服务器的套接字连接之后,将为该套接字创建相应的客户端状态,并将从服务器看做一个连接到主服务器上的客户端来对待,此时从服务器同时具备服务器和客户端两个身份:从服务器可以向主服务器发送命令请求,而主服务器则会向从服务器返回命令回复。
    复制工作接下来的几个步骤都会以从服务器向主服务器发送命令请求的形式来进行。
  • 发送PING命令
    从服务器成为主服务器的客户端之后,做的第一件事就是向主服务器发送一个PING命令。PING命令有两个作用:
    • 检测主从服务器建立起的套接字的读写状态是否正常
    • 因为复制工作接下来的几个步骤都必须是主服务器可以正常处理命令请求的状态下进行,通过发送PING命令可以检查主服务器能否正常处理命令请求
      主服务器对从服务器的PING命令的处理可能会是以下三种情况的其中一种
      • 如果主服务器返回了一个命令回复,但是从服务器不能在规定的时限内读取出命令回复的内容,这种情况需要从服务器建立连向主服务器的套接字
      • 如果主服务器向从服务器返回一个错误,表示主服务器暂时无法处理从服务器的命令请求,这种情况需要从服务器建立连向主服务器的套接字。例如:主服务器正在处理一个超时运行的脚本。
      • 如果从服务器读取到“PONG”的回复,说明主从服务器的网络连接状态正常,可以继续后面的工作。
  • 身份验证
    从服务器在收到主服务器“PONG”回复后,下一步要做的就是决定是否进行身份验证:如果从服务器设置了masterauth选项,那么进行身份验证,否则,不进行身份验证。
    从服务器在身份验证阶段可能会遇到下面几种情况:
    • 如果主服务器没有设置requirepass选项,从服务器没有设置masterauth选项,则复制工作可以继续
    • 如果主服务器设置了requirepass选项,从服务器设置了masterauth选项,并且密码相同则复制工作可以继续,否则,主服务器返回一个invalid password错误
    • 如果主从服务器有一方设置身份证验证,另一方没有设置身份验证,则主服务器都会返回错误信息,
      所有的错误情况都会令从服务器中止目前的复制工作。
  • 从服务器发送端口信息
    身份验证之后,从服务器通过命令REPLCONF listening-port 12345,向主服务发送从服务器的监听端口号。
    主服务器在接收到这个命令之后,会将从服务器的监听端口号记录在从服务器所对应的客户端状态的slave_listening_port属性中。
  • 同步
    在这一步,从服务器向主服务器发送PSYNC命令,执行同步操作,并将自己的数据库更新至主服务器数据库当前所处的状态。
    值得一提的是,在同步操作执行之前,只有从服务器是主服务器的客户端,但是在执行同步操作之后,主服务器也会称为从服务器的客户端:
    • 如果PSYNC执行的是完全重同步,那么主服务器需要成为从服务器的客户端,才能将保存在缓冲区里面的写命令发送给从服务器执行
    • 如果PSYNC执行的是部分重同步,那么主服务器需要成为从服务器的客户端,才能向从服务器发送保存在复制积压缓冲区里面的写命令
    • 主服务成为从服务器的客户端,也是主服务器进行命令传播操作的基础
  • 命令传播
    当同步操作完成后,主从服务器就会进入命令传播阶段,这时主服务器只要一直将自己执行的写命令发送给从客户端,而从客户端只要一直接收并执行主服务器得到命令,就能保证主从服务器的一致。

心跳检测

在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:REPLCONF ACK <replication_offset>,其中replication_offset是从服务器对当前的复制偏移量。
发送REPLCONF ACK对于主从服务器主要有三个作用:

  • 检测主从服务器的网络连接状态
    主从服务器可以通过发送和接受REPLCONF ACK 命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒没有收到从服务器发来的REPLCONF ACK 命令,那么主服务器就知道与从服务器之间的连接出现了问题。
  • 辅助实现min-slaves选项
    redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。
    例如:min-slaves-to-write设置为3,min-slaves-max-lag设置为10,那么从服务器的数量少于3个或者三个服务器的延迟值都大于或等于10秒时,主服务器将拒绝执行写命令,这里的延迟值就是执行INFO replication命令后显示的lag值。
  • 检测命令丢失
    如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失了,那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器将发觉从服务器的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。
    需要注意的是,主服务器向从服务器补发缺失数据的操作的原理和部分重同步操作的原理非常相似,两者的区别在于,补发缺失数据操作在主从服务器没有断线的情况下进行,而部分重同步操作则在服务器断线并重连之后执行。

判断从服务器是否有效

我们知道主服务器和从服务器之间是通过TCP长连接交互数据的,并且会发送心跳包来检测连接有效性;主服务器会记录每个从服务器上次心跳检测成功的时间repl_ack_time,并且定时检测当前时间距离repl_ack_time是否超过了一定超时门限,如果超过则认为从服务器处于失效状态。字段repl_min_slaves_max_lag中存储的就是该超时门限,超时门限可通过min_slaves_max_lag或者min_replicas_max_lag设置,默认是10,单位是秒。
但是主服务器并不是任何时候都会去校验从服务器是否有效的,必须配置repl_min_slaves_to_write和repl_min_slaves_max_lag这两个参数才行。其中字段repl_min_slaves_to_write表示当有效从服务器的数目小于该值时,主服务器会拒绝执行写命令

如何判断从服务器和主服务器是否同步

待补充

学习链接

redis之详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值