Emove

  • 首页
  • 归档
  • 分类
  • 标签

  • 搜索
context 反射 channel LRU BeanDefinition JVM 装饰者模式 MyBatis 快慢指针 归并排序 链表 hash表 栈 回溯 贪心 主从复制 二分查找 双指针 动态规划 AOF RDB 规范 BASE理论 CAP B树 RocketMQ Sentinel Ribbon Eureka 命令模式 访问者模式 迭代器模式 中介者模式 备忘录模式 解释器模式 状态模式 策略模式 职责链模式 模板方法模式 代理模式 享元模式 桥接模式 外观模式 组合模式 适配器模式 建造者模式 原型模式 工场模式 单例 UML 锁 事务 sql 索引

Redis主从复制

发表于 2020-04-11 | 分类于 Redis | 0 | 阅读次数 133

Redis的主从复制

1、主从复制概述

​ 在Redis客户端通过info replication可以查看与复制相关的状态,对于了解主从节点当前状态,以及解决出现的问题都会有帮助

​ 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点复制到从节点

​ 默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或者没有从节点),但一个从节点只能由一个主节点

​ 主从复制的作用主要包括:

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
  2. 故障恢复:当主节点出现问题时,可以由主节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提供Redis服务器的并发量
  4. 高可用基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redsi高可用的基础

2、如何使用主从复制

​ 为了更直观的理解主从复制,在介绍其内部原理之前,先说明我们需要如何操作才能开启主从复制

1、建立复制

​ 需要注意,主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情

​ 从节点开启主从复制,有三种方式:

  1. 配置文件,在从机的Redis配置文件中加入配置

    ################################# REPLICATION #################################
    
    # Master-Slave replication. Use slaveof to make a Redis instance a copy of
    # another Redis server. A few things to understand ASAP about Redis replication.
    #
    # 1) Redis replication is asynchronous, but you can configure a master to
    #    stop accepting writes if it appears to be not connected with at least
    #    a given number of slaves.
    # 2) Redis slaves are able to perform a partial resynchronization with the
    #    master if the replication link is lost for a relatively small amount of
    #    time. You may want to configure the replication backlog size (see the next
    #    sections of this file) with a sensible value depending on your needs.
    # 3) Replication is automatic and does not need user intervention. After a
    #    network partition slaves automatically try to reconnect to masters
    #    and resynchronize with them.
    #
    # 如以下配置
    # slaveof 192.168.3.51 6379
    # slaveof <masterip> <masterport>
    
  2. 启动命令

    redis-server 启动命令后加入 --salveof

  3. 客户端命令

    在Redis客户端中下使用命令 salveof

上述三种方式是等效的。但是第二、三种方式,每当从节点宕机或主动与主节点断开连接后,再次启动需要从新与主节点建立主从关系

2、实例

准备工作:启动两个节点

​ 方便起见,实验所使用的的主从节点是在一台机器上的不同Redis实例,其中主节点监听6379端口,从节点监听6380端口;从节点监听的端口号可以在配置文件中修改。

​ 当两个Redis节点启动后,默认都是主节点。

建立主从关系

​ 此时在6380节点执行slaveof命令,是之变为从节点

slaveof

  1. 首先在从节点查询一个不存在的key

    slaveof-1

  2. 然后在主节点中增加这个key

    slaveof-2

  3. 此时在从节点中再次查询这个key,会发现主节点的操作已经同步到从节点

    slave-3

  4. 从主节点中删除这个key

    slave-4

  5. 此时在从节点中查询这个key,会发现主节点的操作已经同步至从节点

    slave-5

3、断开主从连接

​ 通过slaveof 命令建立主从关系后,可以通过slaveof no one断开。需要注意的是,从节点主动与主节点断开连接后,不会删除已有的数据,只是不再接受主节点新的数据变化。

​ 从节点执行slaveof no one后,打印日志如下所示,可以看出断开复制后,从节点又变回成主节点。

slaveof-3

​ 主节点打印日志如下:

slaveof-4

3、主从复制实现原理

​ 上面一节中,介绍了如何操作可以建立主从关系,本小节将介绍主从复制的实现原理。

​ 主从复制过程大体可以分为三个阶段:

  1. 连接建立阶段(即准备阶段)
  2. 数据同步阶段
  3. 命令传播阶段

1、连接建立阶段

​ 该阶段的主要作用是在主从节点之间建立连接,为数据同步做好准备

1、保存主节点信息

​ 从节点服务器内部维护了两个字段,即masterhost和masterport字段,用于存储主节点的ip和port信息。

​ 需要注意的是,slaveof是异步命令,从节点完成主节点ip和port的保存后,向发送slaveof命令的客户端直接返回OK,实际的复制操作在这之后才开始进行。

​ 在这个过程中,可以看到主节点打印日志如下:

slaveof-5

2、建立Socket连接

从节点每秒1次调用复制定时函数replicationCron(),如果发现了有主节点可以连接,便会根据主节点的ip和port,创建Socket连接。如果连接成功,则:

  1. 从节点:为该Socket建立一个专门处理复制工作的文件时间处理器,负责后续的复制工作,如接受RDB文件,接受命令传播等。
  2. 主节点:接收到从节点的Socket连接后(即accept后),为该Socket创建相应的客户端状态,并将从节点看做是连接到主节点的一个客户端,后面的步骤会以从节点向主节点发送命令请求的形式来进行。

​ 在这个过程中,可以看到从节点打印日志如下:slaveof-6

3、发送Ping命令

​ 从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是:检查Socket是否可用、以及主节点当前是否能够处理请求。

​ 从节点发送ping命令后,可能出现如下3中情况:

  1. 返回pong:说明Socket连接正常,且主节点当前可以处理请求,复制过程继续

  2. 超时:一定时间后,从节点仍未收到主节点的回复,说明Socket连接不可用,则从节点断开Socket连接,并重连

  3. 返回pong以外的结果:如果主节点返回其他的结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开Socket连接,并重连

    在主节点返回pong的情况下,从节点打印日志如下:

    slaveof-7

4、身份验证

​ 如果从节点中设置了masterauth选项,则从节点需要向主节点进行身份验证;没有设置该选项,则不需要验证。从节点进行身份验证是通过向主节点发送auth命令进行的,auth命令的参数即为配置文件中的masterauth的值。

​ 如果主节点设置密码的状态,与从节点masterauth的状态一致(一致是指都存在,且密码相同,或者都不存在),则身份验证通过,复制过程继续。如果不一致,则从节点段开Socket连接,并重连。

5、发送从节点端口信息

​ 身份验证之后,从节点会向主节点发送其监听的端口号(前述例子中为6380),主节点将信息保存到该从节点对应的客户端的slave_listening_port中,该端口信息除了在主节点中执行info Replication时现实以外,没有其他作用。

2、数据同步阶段

​ 主从节点之间的连接建立后,便可以开始进行数据同步,该阶段可以理解为从节点数据的初始化。具体执行的方式是,从节点向主节点发送psync命令(Redis2.8以前是sync命令),开始同步。

​ 数据同步阶段是主从复制最核心的阶段,根据主从节点当前状态的不同,可以分为全量复制和部分复制,下面会有一章专门讲解着两种复制方式以及psync命令的执行过程,这里不再详述。

​ 需要注意的是,在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端。而到了这一阶段及以后,主从节点互为客户端,原因在于,在此之前,主节点只需要响应从节点的请求即可,不需要主动发送请求,而在数据同步阶段和后面的命令传播阶段,主节点需要主动向从节点发送请求(如推送缓冲区中的写命令),才能完成复制。

3、命令传播阶段

​ 数据同步阶段完成后,主从节点进入命令传播阶段,在这个阶段主节点将自己执行的写命令发送给从节点。

​ 在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。由于心跳机制的原理涉及部分复制,因此将在介绍了部分复制的相关内容后独单介绍该心跳机制。

​ 延迟与不一致

​ 需要注意的是,命令传播是异步的过程,即主节点发送写命令后并不会等待从节点的回复;因此实际上主从节点之间很难保持实时的一致性,延迟在所难免。数据不一致的程度,与主从节点之间的网络状况、主节点写命令的执行频率、以及主节点中的repl-disable-tcp-nodelay配置等有关。

​ repl-disable-tcp-nodelay no:该配置作用于命令传播阶段,控制主节点是否禁止与从节点的TCP_NODELAY;默认为no,即不禁止TCP_NODELAY。当设置为yes时,TCP会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送的频率与Linux内核的配置有关,默认配置为40ms,当设置为no时,TCP会立马将主节点的数据发送给从节点,带宽增加但延迟变小。

​ 一般来说,只有当应用对Redis数据不一致的容忍度较高,且主从节点之间网络状态不好时,才会设置为yes,多数情况下使用默认值no

4、【数据同步阶段】全量复制和部分复制

​ 在Redis2.8之前,从节点向主节点发送sync命令请求同步数据,此时的同步方式是全量复制;在Redis2.8及以后,从节点可以发送psync命令请求数据同步,此时根据主从节点当前状态的不同,同步方式可能是全量复制,或部分复制。后文介绍以Redis2.8及以后版本为例。

  1. 全量复制:用于初次复制或其他无法进行部分复制的情况,将主节点中的所有数据都发送给从节点,是一个非常重型的操作
  2. 部分复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。需要注意的是,如果网络中断时间过长,导致主节点没有能够完整的保存中断期间执行的的写命令,则无法进行部分复制,仍使用全量复制

1、全量复制

​ Redis通过psync命令进行全量复制的过程如下:

  1. 从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行全量复制;具体判断过程需要在讲述了部分复制原理后再介绍
  2. 主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
  3. 主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清楚自己的旧数据,然后载入接受的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
  4. 主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态
  5. 如果从节点开启AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态

下面试执行全量复制时,主从节点打印的日志;可以看出日志内容与上述步骤是完全对应的。

主节点的打印日志如下:

master-slave-1

从节点的打印日志如下:

master-slave-2

​ 其中,有几点需要注意:从节点接受了来自主节点的89260个字节的数据;从节点在载入主节点的数据之前要先将老数据清除;从节点在同步完数据后,调用了bgrewriteaof

​ 通过全量复制的过程可以看出,全量复制是非常重型的操作:

  1. 主节点通过bgsave命令fork子进程进行RDB持久化,该过程是非常消耗CPU、内存(页表复制)、硬盘IO的;关于bgsave的性能问题,可以参考深入学习Redis(2):持久化
  2. 主节点通过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗
  3. 从节点清空旧数据,载入新RDB文件的过程是阻塞的,无法响应客户端的命令,如果从节点执行bgrewriteaof,也会带来额外的消耗

2、部分复制

​ 由于全量复制在主节点数据量较大时效率太低,因此Redis2.8开始提供部分复制,用于处理网络中断时的数据同步

​ 部分复制的实现,依赖于三个重要的概念:

  1. 复制偏移量

    ​ 主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增加了N,从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N

    ​ offset用于判断主从节点的数据库状态是否一致:如果二者offset相同,则一致;如果offset不一致,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据。例如:如果主节点的offset是1000,而从节点的offset是500,那么部分复制就需要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置,就是下面要介绍的复制积压缓冲区

  2. 复制积压缓冲区

    ​ 复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小是1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据。注意,无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区

    ​ 在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且是先进先出的,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区

    ​ 由于该缓冲区长度固定且有限,因此可以备份的写命令也有限,当主节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(配置repl-backing-size);例如:如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制

    ​ 从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制:

    • 如果offset偏移量之后的数据,仍然都积压在复制及压缓冲区里,则执行部分缓存
    • 如果offset偏移量之后的数据,已经不在复制积压缓冲区中(数据已被挤出),则执行全量复制
  3. 服务器运行ID(runid)

    ​ 每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),有40个随机数的十六进制字符组成,runid用来唯一识别一个Redis节点。同过info Server命令,可以查看节点的runid。

    runid

    ​ 主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制

    • 如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还有看offset和复制积压缓冲区的情况)
    • 如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制

3、psync命令的执行

​ 在了解了复制偏移量、复制积压缓冲区、节点运行runid之后,本节将介绍psync命令的参数和返回值,从而说明psync命令执行过程中,主从节点是如何确定使用全量复制还是部分复制的。

​ psync命令在执行过程可以参见下图(图片来源:《Redis设计与实现》)

psync执行过程

  1. 首先,从节点根据当前状态,决定如何调用psync命令:
    • 如果从节点之前为执行过slaveof或最近执行了slaveof no one,则从节点发哦是哪个命令为psync ? -1,向主节点请求全量复制
    • 如果从节点之前执行了slaveof,则发送命令为psync ,其中,runid为上次复制的主节点的runid,offset为上次复制截止时从节点保存的复制偏移量
  2. 主节点根据收到的psync命令,即当前服务器的状态,决定执行全量复制还是部分复制:
    • 如果主节点的版本低于Redis2.8,则返回-ERR回复,此时从节点重新发送sync命令执行全量复制
    • 如果主节点版本够新,且runid与从节点发送的runid相同,且从节点发送的offset之后的数据在复制积压缓冲区中,则回复+CONTINUE,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可
    • 如果主节点版本够新,但是runid与从节点发送的runid不同,或从节点发送的offset之后的数据已经不在复制积压缓冲区中(在队列中被挤出了),则回复+FULLRESYNC ,表示将进行全量复制,其中runid表示主节点当前的runid,offset表示主节点当前的offset,从节点保存这两个值,以备使用

4、部分复制演示

​ 在下面的演示中,网络中断几分钟后恢复,断开连接的主从节点进行了部分复制;为了便于模拟网络中断,本例中的主从节点在局域网中的两台机器上

  • 网络中断

    网络中断一段时间后,主节点和从节点都会发现失去了与对方的连接(关于主从节点对超时的判断机制,后面会有说明);此后,从节点便开始执行对主节点的重连,由于此时网络还没有恢复,重连失败,从节点会一直尝试重连

    主节点日志如下:

    断开重连-1

    从节点日志如下:

    断开重连-2

  • 网络恢复

    网络恢复后,从节点连接主节点成功,并请求进行部分复制,主节点接受请求后,二者进行部分复制以同步数据

    主节点日志如下:

    断开恢复-1
    从节点日志如下:

    断开恢复-2

5、【命令传播阶段】心跳机制

​ 在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用

1、主 -> 从:PING

​ 每隔指定的时间:主节点会向从节点发送Ping命令,这个Ping命令的作用,主要是为了让从节点进行超时判断。

​ Ping发送的频率由repl-ping-slave-period参数控制,单位是秒,默认值是10s。

​ 关于该Ping命令究竟是由主节点发给从节点,还是相反,有一些争议;因为在Redis官方文档中,对该数据的注释中说明是从节点向主节点发送Ping命令,如下图所示
ping

​ 但是根据该参数的名称(含有ping-slave),以及代码实现,我认为该ping命令是主节点发给从节点的。相关代码如下:

ping-source-code

2、从 -> 主:REPLCONF ACK

​ 在命令传播阶段,从节点会向主节点发送REPLCONF ACK命令,频率是每次一秒;命令格式为:REPLCONF ACK,其中offset值从节点保存的复制偏移量。REPLCONF ACK命令的作用包括:

  1. 试试检测主从节点的网络状态:该命令会被主节点用于复制超时的判断。此外,在主节点中使用info Replication,可以看到其从节点的状态中的lag值,代表的是主节点上次收到该REPLCONF ACK命令的时间间隔,在正常情况下,该值应该是0或1,如下如所示:

    lag

  2. 检测命令丢失:从节点发送了自身的offset,主节点会与自己的offset对比,如果从节点数据缺失(如网络丢包),主节点会推送缺失的数据(这里也会利用复制积压缓冲区)。注意,offset和复制积压缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情况,区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的

  3. 辅助保证从节点的数量和延迟:Redis主节点中使用min-slaves-to-write和min-slaves-max-lag参数,来保证主节点在不安全的情况下不会执行写命令。所谓不安全,是指从节点数量太少,或延迟太高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。而这里从节点延迟值的获取,就是通过主节点接收到REPLCONF ACK命令的时间来判断的,即前面所说的info Replication中的lag值

6、总结

​ 下面回顾下本文的主要内容:

  1. 主从复制的作用:宏观的了解主从复制是为了解决什么样的问题,即数据冗余,故障恢复,读负载均衡等
  2. 主从复制的命令操作:即slaveof命令
  3. 主从复制的原理:主动复制包括了连接建立阶段,数据同步阶段,命令传播阶段;其中数据同步阶段有全量复制和部分复制两种数据同步方式;命令传播阶段,主从节点直接有Ping和REPLCONF ACK命令互相进行心跳检测

​ 主从复制虽然解决或缓解了数据冗余,故障恢复、读负载均衡等问题,但其缺陷仍很明显:故障恢复无法自动化,写操作无法负载均衡;存储能力受到了单机的限制;这些问题的解决,需要哨兵和集群的帮助,我将在后面的文章中介绍,欢迎关注。

原文地址

深入学习Redis(3):主从复制

# context # 反射 # channel # LRU # BeanDefinition # JVM # 装饰者模式 # MyBatis # 快慢指针 # 归并排序 # 链表 # hash表 # 栈 # 回溯 # 贪心 # 主从复制 # 二分查找 # 双指针 # 动态规划 # AOF # RDB # 规范 # BASE理论 # CAP # B树 # RocketMQ # Sentinel # Ribbon # Eureka # 命令模式 # 访问者模式 # 迭代器模式 # 中介者模式 # 备忘录模式 # 解释器模式 # 状态模式 # 策略模式 # 职责链模式 # 模板方法模式 # 代理模式 # 享元模式 # 桥接模式 # 外观模式 # 组合模式 # 适配器模式 # 建造者模式 # 原型模式 # 工场模式 # 单例 # UML # 锁 # 事务 # sql # 索引
正则表达式匹配
盛最多水的容器
  • 文章目录
  •   |  
  • 概览
林亦庭

林亦庭

less can be more

87 文章
11 分类
54 标签
RSS
Github
Creative Commons
© 2021 林亦庭
粤ICP备20029050号