Redis数据库——主从复制

Redis数据库——主从复制

大家好,这里是编程Cookbook,关注公众号「编程Cookbook」,获取更多面试资料。本文详细介绍Redis的主从复制模式,包括作用,原因,工作原理,同步流程等。

主从复制

什么是 Redis 主从复制?

Redis 主从复制是一种将主节点(master)上的数据同步到其他从节点(slave)Redis 实例的机制。数据复制是单向的,只能从主节点同步到从节点,或者层级复制。

r-11-1.png

  • 主节点(Master):负责写操作。
  • 从节点(Slave):负责读操作,复制主节点的数据。
  • 默认配置:每台 Redis 服务器默认启动时都是主节点(Master),但是通过配置,可以将某些 Redis 服务器设置为从节点(Slave),从节点是通过配置来指定的,不是自动的。
  • 树形结构:一个主节点可以有多个从节点,一个从节点只能有一个主节点。

节点数量:

  • 1个主节点,多个从节点 是标准的主从复制架构。
  • 多个主节点多个从节点 配置,适用于 Redis 集群模式,这种模式下每个主节点负责一个数据分片,每个分片可以有多个从节点。
  • 主节点的数量通常较少(通常是一个),而从节点的数量可以根据需要增加。

为什么需要 Redis 主从复制?

在单机模式下,存在以下两个问题:

  1. 服务器宕机:一旦服务器宕机,数据会直接丢失。
  2. 提高系统的负载能力:实现读写分离,通过增加多个从节点来分担主节点的读取请求。

通过主从复制,数据可以保存在多个服务器上,并且确保每个服务器的数据是同步的。即使某一台服务器宕机,也不会影响服务的持续运行,Redis 可以继续提供高可用性,并实现数据的冗余备份。

有的文章中还提到,单机模式还存在内存限制问题:单台服务器的内存有限,无法进行无限制的扩展。

  • 但是主从复制本身并不会直接解决内存问题,在 Redis 主从复制 中,主节点(Master)和从节点(Slave)存储的是 相同的数据,因为从节点是通过复制主节点的写操作来保持数据同步的。这意味着,从节点的内存和主节点的内存内容基本相同。只是从节点主要用来处理读取请求,而主节点处理写入请求,可以通过增加多个从节点来分担读取压力,从而提高系统的负载能力
  • 单节点的内存限制问题需要通过 Redis 集群模式(数据分片)来解决,通过在多个 Redis 节点之间分布数据来突破单机的内存限制。

Redis 主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,这是持久化以外的另一种数据备份方式。

  2. 故障恢复:当主节点(master)出现问题时,可以由从节点(slave)提供服务,实现快速恢复故障,从而保证服务不间断,提升服务的冗余性。

  3. 读写分离:主节点主要负责写操作,从节点主要负责读操作,能够有效提高服务器的负载能力。在读写分离的情况下,可以根据需求动态调整从节点数量,提升系统的处理能力。

  4. 负载均衡:通过主从复制,可以实现负载均衡。主节点提供写服务,从节点提供读服务,在读多写少的场景下,通过多个从节点分担读取负载,极大提高 Redis 的并发能力。

  5. 高可用的基石:主从复制是 Redis 哨兵(Sentinel)和 Redis 集群(Cluster)实现高可用性的基础。因此,主从复制是 Redis 高可用架构的关键组成部分。

主从复制工作原理

主从复制的完整工作流程分为以下三个阶段,每个阶段都有自己的内部工作流程。接下来,我们将对这三个过程进行详细讨论:

  • 建立连接过程:从节点(slave)向主节点(master)建立连接的过程。
  • 数据同步过程:主节点向从节点同步数据的过程。
  • 命令传播过程:这是反复同步数据的过程,用于保持主从节点的数据一致性。

第一阶段:建立连接过程

建立连接过程的详细步骤如下:

  1. 设置主节点地址和端口:从节点(Slave)首先需要配置主节点(Master)的地址和端口信息,以便能够建立连接。

  2. 建立 socket 连接:从节点通过网络与主节点建立 TCP socket 连接,这是 Redis 主从复制建立连接的基础。

  3. 发送 PING 命令保持连接:为了确保连接的持续性,从节点会定期向主节点发送 PING 命令。如果连接断开或丢失,主节点会在某些情况下重新启动连接过程。

  4. 身份验证:如果 Redis 配置了 密码认证,从节点需要通过发送 AUTH 命令来验证自己是否有权限进行复制和同步。此步骤确保从节点有足够的权限访问主节点。

  5. 发送端口信息:从节点在建立连接后,会将自己的 IP 地址和端口 发送给主节点。这样,主节点就能知道从节点的具体地址,便于在后续的同步过程中建立数据传输。

  6. 保存主从节点信息:一旦连接成功,主节点将记录从节点的连接信息(包括从节点的 IP 和端口等),并为后续的数据同步过程做好准备。从节点也会保存主节点的连接信息,以便在故障恢复时重新连接。

  • 主动连接和被动连接:在 Redis 中,从节点会主动连接主节点。主节点通常不会主动连接从节点。

  • 连接持续性:通过 心跳机制PING 命令)保持连接,确保主从节点之间的连接稳定并及时检测异常。

第二阶段:数据同步阶段

数据同步阶段需要经过使用 RDB 快照的全量复制(第一次连接时才会用)和积压缓冲区数据的部分复制两阶段:

  1. 全量复制

    • 当从节点第一次连接主节点时,主节点会执行 全量复制,即将 主节点的所有数据(包括所有数据库的键值对)一次性发送给从节点。这是不可避免的,因为从节点需要完全初始化自己的数据副本。
    • 这个过程通常是通过 RDB 快照 来完成的,主节点会将 RDB 文件中的数据发送给从节点。
  2. 部分复制

    • 完成全量复制后,从节点会继续监听主节点,接收主节点的 增量数据
    • 部分复制 是指在从节点完成全量复制后,主节点会将其 复制积压缓冲区 中的增量数据(即主节点自从节点上次同步后新增的数据)发送给从节点,确保从节点的数据是最新的。
    • 部分复制的过程是实时进行的,主节点会将写入操作(如 SETHSET 等命令)记录到复制积压缓冲区,并将这些命令同步给所有的从节点。
  3. 复制缓冲区

    • 主节点使用 复制积压缓冲区 来存储自从节点上次同步后的数据变更。当从节点在某段时间内无法同步时,主节点会在缓冲区中保留增量数据,确保即使从节点滞后,依然能够通过缓冲区补齐差距。
    • 这个缓冲区的大小是有限的,通常由配置项 repl-backlog-size 控制。当缓冲区满时,较旧的数据会被丢弃,因此如果从节点长时间落后,可能无法完全通过复制积压缓冲区来恢复丢失的数据。

值得强调的是,从节点并非每次都会进行全量复制,只有第一次连接时会进行全量复制,后续大部分时间是通过 部分复制 来维持主从同步。

  • 全量复制:从节点首次连接时获取主节点所有数据,通常通过 RDB 快照进行。
  • 部分复制:全量复制后,主节点将增量数据(写操作)实时同步给从节点,保证数据的一致性。
  • 复制缓冲区:主节点的增量数据存储缓冲区,帮助从节点追赶主节点的更新。

第三阶段:命令传播阶段

命令传播阶段是主从数据同步的持续过程,当主节点的数据库被修改时,从节点需要更新数据,以保持主从数据一致性。

  1. 主节点 接收到数据变更命令时,会实时将这些命令发送给 从节点
  2. 从节点接收到命令后,会执行相应操作,确保主从数据一致。

以下是命令传播阶段的详细介绍:

  1. 主节点处理写操作

    当主节点接收到客户端的写请求时(如 SETDELLPUSH 等),主节点会:

    • 执行数据变更操作。
    • 将这些变更操作(即写命令)广播给所有从节点。
  2. 主节点将命令发送给从节点

    主节点会将所有数据变更命令通过 命令传播 发送给它的从节点。命令的传播是异步的,并且是实时的。具体来说:

    • 主节点会将数据变更命令(如写命令)按顺序发送到所有的从节点。
    • 从节点在接收到这些命令后,会将命令应用于自己的数据副本。
  3. 从节点接收并执行命令

    每个从节点会在收到主节点的命令后,立即执行对应的操作。这个过程保证了从节点的数据与主节点的数据始终保持一致。

    • 从节点执行命令:当从节点收到来自主节点的写命令时,它会在自己的数据库上执行该命令,更新自身的数据副本。
    • 命令的实时性:这些命令是实时发送的,因此,主节点的任何数据修改都将立刻传播到从节点。
  4. 传输阶段的部分复制
    “第二阶段:数据同步阶段”中的“部分复制”中指出,Redis 使用了复制积压缓冲区,这个缓冲区存储主节点和从节点之间的增量数据(如写命令)。同时,如果从节点由于某些原因掉线或者暂时无法同步数据,主节点也会将积压的命令数据发送给积压缓冲区。

    • 在命令传播阶段,如果发生 断网网络抖动,可能导致主从连接中断(connection lost)。
    • 即便如此,主节点会继续往 复制缓冲区(replbackbuffer) 写入数据。
    • 从节点会继续尝试重新连接到主节点(connect to master)。
    • 一旦从节点成功重新连接,它会将自己的 runid复制偏移量 发送给主节点,并执行 PSYNC 命令来同步数据。
    • 如果主节点判断偏移量在复制缓冲区的有效范围内,主节点会返回 continue 命令,并将复制缓冲区中的数据发送给从节点。
    • 从节点接收到数据后,会执行 BGREWRITEAOF,通过恢复数据来同步主从状态。

    这个过程确保了主从数据的一致性,即使在网络中断等情况下,主节点和从节点也能够通过积压缓冲区重新同步数据。

保持主从数据一致性:通过实时的命令传播机制,主节点和从节点的状态始终保持同步。即使在高并发的环境中,主节点的修改会迅速传递到所有从节点,确保数据一致性。

命令传播的异步性:虽然命令传播是实时的,但它是 异步的。这意味着:

  • 主节点发送命令后,不需要等待从节点执行完命令才继续处理其他请求。
  • 从节点独立地执行命令,并在自己的数据库中更新数据,主节点不需要等待确认。

主从复制(全量复制 + 部分复制)流程

r-11-2.png

  • 全量复制:1 - 4是全量复制。指从节点第一次连接主节点时,主节点将全部数据通过 RDB 文件发送给从节点。这是初始化过程,无法避免。
  • 部分复制:5 - 8是部分复制。在全量复制后,主节点会持续接收客户端的数据更新,主节点的 offset 会随时变化。这些更新会通过复制缓冲区发送给从节点。部分复制能够保证主从节点数据的一致性,避免每次都进行全量复制。

主从复制的过程可以分为全量复制和部分复制,下面是详细的流程讲解:

1. 从节点发送指令 psync ? 1

  • 第一次连接:当从节点第一次连接到主节点时,它并不知道主节点的 runidoffset,因此从节点发送 psync ? 1 指令,表示它希望获取主节点的全部数据
  • psync ? 1 触发了全量复制过程。

2. 主节点执行 bgsave,生成 RDB 文件

  • 主节点开始执行 bgsave 操作,生成 RDB 文件并记录当前的复制偏移量 offset
  • 此时,主节点会保存自己的 runidoffset

3. 主节点返回 +FULLRESYNC runid offset

  • 主节点将自己的 runidoffset 通过 +FULLRESYNC runid offset 指令返回给从节点,并通过 socket 发送 RDB 文件给从节点。
  • 从节点接收到 +FULLRESYNC 后,会保存主节点的 runidoffset,然后清空当前的所有数据,通过 socket 接收 RDB 文件并开始恢复数据。

4. 从节点执行全量复制

  • 从节点通过接收到的 RDB 文件恢复数据,完成 全量复制

5. 从节点发送指令 psync runid offset

  • 在全量复制完成后,从节点已经获取到主节点的 runidoffset,接下来它会发送 psync runid offset 指令。
  • 这时,从节点通过指令向主节点询问是否可以继续进行部分复制。

6. 主节点接收 psync 指令,判断 runidoffset

  • 主节点接收到 psync 指令后,首先判断从节点提供的 runid 是否匹配。如果 runid 不匹配,说明从节点重启过,这时会重新开始全量复制
  • 然后主节点判断从节点提供的 offset 是否在复制缓冲区中。如果 offset 不在复制缓冲区中,意味着从节点在重新连接时,复制缓冲区已溢出,主节点会继续执行全量复制。

7. 如果 runidoffset 校验通过

  • 如果 runidoffset 校验通过,且从节点的 offset 与主节点的 offset 相同,则从节点无需任何额外操作。
  • 如果 offset 不同,主节点会发送 +CONTINUE offset,将复制缓冲区中的数据发送给从节点,直到从节点的 offset 与主节点一致。

8. 从节点接收到 +CONTINUE,恢复数据

  • 从节点收到 +CONTINUE 后,会保存主节点的 offset,然后通过 socket 接收主节点的数据,并执行 bgrewriteaof,通过重写 AOF 文件恢复数据。

补充

在 Redis 的 部分复制 流程中,确实有三个核心要素非常重要,它们分别是 服务器的运行 ID(run ID)复制偏移量(offset)复制积压缓冲区。这些元素的配合保证了从节点能够在断线重连后,尽量使用增量数据来恢复,而不是每次都进行全量复制。下面是每个核心概念的详细介绍:

1. 服务器的运行 ID(run ID)

  • 定义runid 是 Redis 在启动时自动生成的一个随机字符串,通常是 40 个十六进制字符(例如 f2e87b72f3c4d4d83df456f5e673be2baedb6e4f),用于唯一标识一个 Redis 节点。每次 Redis 重启时,runid 都会重新生成,因此不同的启动周期会有不同的 runid

  • 作用

    • runid 用于标识 Redis 主节点(master)和从节点(slave)之间的唯一连接关系。它对于主从复制中的部分复制至关重要。
    • 部分复制 过程中,如果从节点断开连接后重新连接,主节点会检查从节点传递过来的 runid 是否和当前主节点的 runid 相同。如果相同,则表示从节点与主节点的身份一致,可以进行部分复制。
    • 如果 runid 不匹配(比如从节点重启了),主节点会认为从节点是一个新的实例,会重新进行 全量复制
  • 如何查看 runid

    • 可以使用 INFO replication 命令查看 Redis 节点的 runid,例如:
      redis-cli INFO replication
      
      输出中的 master_run_idrun_id 表示主节点的运行 ID。
  • 全量复制 vs 部分复制

    • 当从节点断开重连时,如果其保存的 runid 与主节点当前的 runid 不匹配(即主节点已重启),主节点会触发全量复制。
    • 如果 runid 匹配,且复制的偏移量(offset)在复制缓冲区中有效,从节点可以通过增量方式继续复制数据(部分复制)。

2. 复制偏移量(offset)

  • 定义offset 是主节点用来标识当前数据流位置的一个数字,表示主节点已经发送了多少条数据。每当一个命令被写入主节点的 AOF 文件或复制缓冲区时,都会为该命令分配一个唯一的 offset

  • 作用

    • offset 主要用来标识复制缓冲区中的数据流进度,确保主从节点的数据同步。
    • 它帮助从节点在恢复时,知道自己已经处理到哪个数据点,并通过比较主节点的 offset 和从节点的 offset 来确定是否需要继续同步数据。
    • 如果从节点在复制过程中断开连接,断开时的 offset 会被保存,以便重新连接时,主节点可以继续从断开的 offset 处发送数据。
  • 如何使用 offset

    • 主节点记录发送给从节点的命令的 offset,并在从节点重新连接时,与从节点发送的 offset 对比。如果从节点的 offset 过期或不在复制缓冲区中,主节点会执行全量复制。
    • 如果从节点的 offset 有效,主节点会继续发送增量数据(即复制缓冲区中尚未发送的数据)。

3. 复制积压缓冲区(Replication Backlog)

  • 定义:复制积压缓冲区是 Redis 为了处理 部分复制 而引入的一个内存缓冲区。它是一个先进先出(FIFO)的队列,用于存储主节点最新的数据变更命令。当主节点接收到写命令时,它会将命令记录到该缓冲区中,并为每个命令分配一个 offset

  • 缓冲区的大小

    • 默认情况下,复制积压缓冲区的大小是 1MB。可以通过 repl-backlog-size 配置项调整其大小。
    • 若主节点的写操作量大于缓冲区的大小,早期的命令会被挤出缓冲区,导致从节点无法通过部分复制恢复这些数据。
  • 作用

    • 部分复制 中,当从节点与主节点断开连接时,主节点会继续向复制积压缓冲区写入数据。当从节点重新连接时,主节点会检查从节点提供的 offset 是否在复制缓冲区中。如果在缓冲区中,主节点将从缓冲区发送增量数据给从节点,直到从节点的数据同步完成。
    • 如果复制缓冲区中的数据被挤出(因为数据量大于缓冲区的大小),主节点将无法为从节点提供增量数据,导致需要执行 全量复制
  • 为什么复制缓冲区可能导致全量复制

    • 如果主节点在某一时刻接收到大量的写命令,这些命令可能会迅速填满复制积压缓冲区,超出缓冲区的大小。当从节点与主节点断开连接时,部分数据可能已经被覆盖,无法恢复。
    • 这种情况会导致从节点无法获取完整的增量数据,从而触发全量复制,恢复数据。

4. 心跳机制

在 Redis 中,心跳机制(Heartbeat Mechanism)机制非常重要,尤其是在高并发环境中,它帮助 Redis 及时发现网络问题或连接中断,保证主从复制的稳定性。它是通过定期发送 PING 命令来实现的。

作用:

  1. 保证复制的稳定性:Redis 的主从复制机制要求主节点和从节点之间保持稳定的连接。通过心跳机制,Redis 可以及时发现连接中断或网络问题,并采取适当的恢复措施。

  2. 防止连接超时:Redis 默认的网络连接有超时限制,心跳机制可以防止由于长时间没有数据传输而导致的连接超时。

  3. 实时检测异常:在高负载的情况下,Redis 可以通过心跳命令及时发现连接丢失,避免在从节点掉线后还继续同步数据。

工作原理:
Redis 的心跳机制主要通过定期发送 PING 和 PONG 命令来保持主从节点之间的连接活跃,确保在主从数据同步过程中连接不被中断。以下是 Redis 心跳机制的详细工作原理:

  1. 主节点和从节点的心跳

    • 从节点:从节点定期向主节点发送 PING 命令,表明它仍然活跃并且希望保持与主节点的连接。
    • 主节点:主节点也会定期向从节点发送 PING 命令,以确认从节点的状态。如果从节点没有响应,则可能意味着连接存在问题,主节点会中断该连接并重新尝试连接。
  2. 确保连接活跃

    • 在主从复制的过程中,Redis 通过心跳命令确保主节点和从节点之间的连接一直保持活跃,即使在没有任何数据传输的情况下。
    • 如果主节点与从节点之间长时间没有数据交互,Redis 会通过发送心跳命令保持连接的持续性,防止连接因超时被关闭。
  3. 检测和恢复连接

    • 如果从节点在指定时间内没有回应主节点的 PING 请求,主节点会认为该从节点的连接已经丢失,可能会尝试重新建立连接。
    • 如果主节点没有回应从节点的心跳请求(PING),从节点会认为主节点掉线,并可能重新连接或进行其他恢复操作。
  4. 故障检测与容错

    • 故障检测:心跳机制帮助 Redis 及时发现故障,尤其是在 Redis Sentinel 集群模式中,心跳机制非常重要。Redis Sentinel 通过心跳检测主节点的健康状况,并在主节点宕机时自动触发故障转移。

    • 容错:当从节点检测到主节点的心跳丢失时,从节点会发起连接重试。如果主节点出现问题且无法恢复,Redis Sentinel 会通过投票机制选举新的主节点,确保系统的高可用性。

  5. 高效性

    • Redis 通过非常轻量级的 PINGPONG 命令来实现心跳机制,这些命令的响应时间极短,不会引起明显的性能开销。
    • PING 命令只是单纯地向对方发送一个空的请求,目的是检查连接是否正常,且它不会有额外的负载。

相关配置:

  • repl-ping-slave-period:控制从节点向主节点发送 PING 命令的时间间隔(单位:秒)。默认值为 10 秒。可以根据实际需求调整这个间隔,以确保主从节点的连接稳定。

  • repl-timeout:设置主从复制中等待从节点响应的超时时间。如果在这个时间内从节点未响应,则认为连接出现故障。

  • timeout:配置 Redis 客户端连接的超时时间,防止连接因长时间未活动而被断开。

总结:
Redis 的心跳机制通过定期的 PING 命令来保持主从节点之间的连接活跃,确保在主从数据同步过程中,连接不会因网络问题或长时间无数据传输而中断。这个机制不仅有助于维持连接的稳定性,还有助于故障检测和自动恢复,从而保障 Redis 的高可用性。

主从复制常见问题及优化

1. 复制延迟

  • 问题:主节点和从节点之间的同步存在延迟,尤其是在高负载的情况下,主节点的数据变更可能无法及时同步到从节点,导致从节点数据滞后。
  • 优化
    • 调整复制缓冲区大小:增加复制积压缓冲区大小(repl-backlog-size),以存储更多的命令,从而避免从节点由于网络问题丢失数据。
    • 监控 replica-lag:定期监控 replica-lag 指标,评估从节点与主节点的同步差距。如果延迟过高,考虑加大带宽或优化主节点的写入操作。
    • 使用异步复制:尽可能将从节点的读操作放在主节点的负载较低时进行,减轻主节点压力。

2. 全量复制性能问题

  • 问题:当从节点断线重新连接时,必须执行全量复制,这会导致主节点的压力骤增,并且从节点需要较长时间恢复数据,影响整个系统的性能。
  • 优化
    • 启用复制积压缓冲区:在主节点和从节点的网络连接中,启用复制积压缓冲区。这样,从节点可以在短时间内恢复连接时,通过复制缓冲区获取丢失的数据,而不需要全量复制。
    • 增加从节点的复制并发度:可以增加从节点的数量,分摊负载,避免单个从节点恢复时的瓶颈。
    • 合理配置 RDB 和 AOF:优化 RDB 文件和 AOF 文件的持久化配置,避免全量复制时的性能瓶颈。

3.网络带宽和连接稳定性问题

  • 问题:在高并发和大量数据写入的情况下,网络带宽可能成为瓶颈,导致复制数据同步速度过慢,甚至导致连接丢失。
  • 优化
    • 网络优化:优化网络连接,使用更高带宽的网络,确保主从节点之间的连接稳定。
    • 提高硬件性能:如果可能,升级主节点和从节点的硬件配置,减少磁盘 IO 和 CPU 使用,提升数据处理能力。
    • 压缩数据传输:使用数据压缩技术,在主从节点之间传输数据时减少网络带宽占用。

4. 主从节点的数据一致性问题

  • 问题:在某些极端情况下(如断电、网络分区等),主从节点可能会出现数据不一致的情况,导致恢复时的错误。
  • 优化
    • 启用持久化机制:确保 Redis 配置了 RDB 和 AOF 持久化,避免数据丢失。
    • 使用 Redis Sentinel:启用 Redis Sentinel 对主节点进行监控,自动检测主节点故障并进行故障转移,保持系统的数据一致性。
    • 配置复制积压缓冲区:通过合理设置复制积压缓冲区(repl-backlog-size),可以避免在短时间内数据丢失。通过增加缓冲区的大小,可以容忍更多的数据丢失窗口。

5. 从节点负载过高

  • 问题:如果从节点的读请求过多,可能会导致从节点的负载过高,影响同步性能,甚至导致从节点无法及时跟上主节点的数据变更。
  • 优化
    • 负载均衡:将读请求均匀分配到多个从节点,避免单个从节点压力过大。
    • 读写分离:将读操作分配到从节点,写操作只由主节点处理,减轻主节点负担。
    • 优化查询性能:通过使用更高效的数据结构和命令,优化从节点的查询性能,减少请求处理时间。

6. 故障恢复和故障转移问题

  • 问题:主节点发生故障时,Redis 需要能够自动将从节点提升为主节点,而这一过程可能会带来一定的恢复时间,导致系统不可用。
  • 优化
    • 配置 Redis Sentinel:通过 Redis Sentinel 监控主节点的健康状况,在主节点发生故障时,自动将一个从节点提升为新的主节点,减少故障恢复时间。
    • 启用自动故障转移:在 Redis 集群中,启用自动故障转移机制,以便在某个节点发生故障时,能够迅速恢复服务,保证高可用性。