Redis分布式系统中的主从复制

 

  本篇文章主要对Redis的主从复制进行讲解。主要分析复制的原理,包括:建立复制、全量复制、部分复制、全量复制、心跳检测等。希望本篇文章会对你有所帮助。

文章目录

一、主从复制简介 

二、配置主从复制模式

断开主从复制

安全性

只读

传输延迟

三、拓扑结构

四、主从复制原理

主从节点建立复制流程图

数据同步psync 

psync运行流程 

全量复制流程

部分复制流程

实时复制

五、总结

主从复制解决的问题

主从复制的特点

主从复制的优缺点


🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️

👀 专栏:Redis 👀

💥 标题:Redis分布式系统中的主从复制💥

❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️

一、主从复制简介 

  在学习的过程中,大部分接触到的都是单机架构的。所谓单机架构,就是把所有的服务程序都部署在一台机器上。单机架构使用起来方便,维护起来较为简单。但是随着数据膨胀式增长的时代,单机架构在某些场景下已经满足不了我们的需求了。单机架构的缺点不止于此:

  • 可用性问题:如果这个机器挂了,意味着服务就中断了;
  • 性能问题:性能/支持的并发量也是比较有限的。

  为了解决上述的单点问题,在分布式系统中通常会把数据复制多个副本部署到其他服务器,满足故障恢复和负载均衡等需求。Redis也是如此:多个服务器同时部署上Redis服务,从而构成一个Redis集群。Redis集群可以为分布式系统提供更稳定、更高效的数据存储服务。它为我们提供了复制的功能,实现了相同数据的多个Redis副本。

  但是具体怎么在多个服务器上同时部署Redis服务呢?当今主流的部署方式有如下三种:

  • 主从模式(Master-Slave): 主从模式是最简单的部署方式之一。在这种模式下,一个 Redis 主节点(Master)负责处理写操作和读操作,而一个或多个 Redis 从节点(Slave)则复制主节点的数据,并且只负责处理读操作。主节点负责处理所有写操作,并将写操作同步到所有从节点。主从模式提高了读取性能和可用性,但写入性能仍受限于主节点。
  • 主从+哨兵模式(Master-Slave with Sentinel): 主从+哨兵模式在主从模式的基础上增加了哨兵(Sentinel)节点的部署。哨兵节点负责监控主节点和从节点的健康状态,并在主节点不可用时自动将一个从节点晋升为新的主节点,以保证系统的高可用性。这种方式可以自动处理主节点的故障转移,提高了系统的可靠性。
  • 集群模式(Cluster): 集群模式是用于横向扩展 Redis 的方式之一。在集群模式中,数据被分片存储到多个节点上,每个节点负责存储部分数据片段。集群模式提供了更好的水平扩展性和负载均衡能力,可以处理大规模数据和高并发请求。此外,集群模式还具备数据自动重分布和故障转移等特性,以保证数据的高可用性和一致性。

  这里大家现有一个概念,本篇文章会对主从模式进行详解。后续会详解另外两种模式。

  主从模式简单来说:多台服务器上都部署了Redis服务,但只有一个Redis服务为主节点,其余的均为从节点。主节点可读可写,从节点只能读。我们将所有的写操作都请求到了主节点上,然后主节点把修改的数据同步到从节点上。

  现在问题来了:多台服务器上的Redis服务要保存同一份数据,这些服务器之间的数据如何保持一致性呢?答案是主节点把数据同步到从节点。如果我改了从节点的数据,是否是把从节点的数据往主节点上同步呢?Redis主从模式中,从节点上的数据不允许修改!!只能读取数据!!更准确的说,主从模式主要是针对"读操作",写操作的请求仍然是在一个服务器上。注意:主从复制中的主节点只能搞一个。实际业务场景中读操作往往就是比写操作更频繁!

  现在再来看:之前只是单个redis服务器节点,此时这个机器挂了整个redis就挂了。上述这个主从结构,多台机器上同时部署了Redistribution服务,这些Redis的机器不太可能"同时挂了"。如果是挂掉了某个从节点,其实也没啥影响。此时继续从主节点或者其他从节点读取数据,得到的效果完全相同!如果是挂掉了主节点呢?还是有一定影响的。从节点只能读数据,如果需要写数据就没得写了,数据也就得不到更新了。当然可能想到部署多个主节点呢?多个主节点就不得不考虑往那个主节点上写数据,主节点之间同步数据,主节点把数据同步到从节点上等等问题,就更加的麻烦了,同时性能也是个问题。

二、配置主从复制模式

  配置redis主从结构,首先需要启动多个redis服务器。正常对于分布式系统来说,每个redis服务器程序应该在一个单独的机器上。但是由于条件有限,小编只能在一台服务上部署多个Redis服务。

  正常情况下,默认的redis-server绑定的端口是6397(可在/etc/redis/redis.conf下查看),如下图:

  现在我们需要启动多个服务端,所以需要首先copy多份该配置文件,然后对该配置文件的端口进行修改。当然,也可以在启动程序的时候通过命令行--port选项来指定端口号。

  本篇文章采用一主两从的模式来讲解,所以就copy了两个配置文件并进行修改。如下:

  首先修改端口,可自定义选择,其次是配置daemonsize选项。如下图:

  此时我们只是进行简单的修改,并没有指定设置主节点,谁是从节点。参与复制的Redis实例划分为主节点(master)和从节点(slave)。每个从结点只能有一个主节点,而一个主节点可以同时具有多个从结点。复制的数据流是单向的,只能由主节点到从节点。配置复制的方式有以下三种:

  1. 在配置文件中加入 slaveof {masterHost} {masterPort}随 Redis启动生效。
  2. 在redis-server启动命令时加入--slaveof {masterHost} {masterPort}生效。
  3. 直接使用redis命令: slaveof {masterHost} {masterPort}生效。

  这里推荐大家使用第一种方法,后续启动Redis一直生效。使用命令的方式,需要每次启动Redis客户端连接服务端时重新指定。

  上述第一种指定的方式,在配置文件中加入了:slaveof 127.0.0.1 6379。标示本机的6379的Redis服务为主节点。redis服务器的配置文件改完了之后,就需要重新启动才能生效!ps:如果是使用service redis-server start 这种方式启动的,就必须使用service redis-server stop 来进行停止。使用kill 的方式,这种停止redis-server的方式是和过直接运行redis-server 命令的方式搭配的。如果是使用service redis-server start 这种方式启动的,使用kill -9的方式,kill掉之后这个redis-server进程能自动启动。使用 service redis-server stop 命令将以正常的方式终止 Redis 服务器,并处理相关的清理工作,包括释放占用的资源和关闭连接。这样可以确保服务器的数据安全和服务的稳定性。

  接下来,我们可以通过 redis-cli slave1.conf来启动redis客服端,该客户端的配置默认就是slave1.conf中的配置。我们也可以通过 netstat -nlpt 命令确保两个 Redis 实例已经正确启动,并且监听了相应的端口。当我们启动redis从节点后,从节点启就会和主节点建立上 tcp 连接,主节点就相当于服务器,从节点就相当于客户端了。主节点同步数据也就是通过这个tcp连接。

  此时我们启动客户端,如下:

  注意:上述不指定的情况下,默认启动时使用的配置文件为:/etc/redis/redis.conf,该配置文件为主节点。我们在写入两条数据:

  再次启动从头结点来获取主节点设置的数据,如下图:

  从节点写数据是被禁止的,如下图:

  从运行结果中看到复制已经工作了,针对主节点6379的任何修改都可以同步到从节点6380中,复制过程如图所示。


  通过以上步骤,我们可以验证复制关系已经建立成功,并且主节点对数据的修改可以同步到从节点中。这样就实现了 Redis 的主从复制功能。

   当使用 Redis 命令行客户端连接到 Redis 服务器后,在客户端可以通过 info replication 命令来查看复制相关的状态信息。具体如下:

  上图为主节点,端口号为6379。以下是一些常见字段的解释:

  1. role:表示当前 Redis 节点的角色,可能是 master(主节点)或 slave(从节点)。
  2. connected_slaves:显示连接到当前主节点的从节点数量。
  3. Slave0和Slave1: 两个从节点的信息如下:

    Slave0: IP地址为127.0.0.1,端口号为6380,状态为在线(state=online),当前复制偏移量为294,滞后(lag)为1。

    Slave1: IP地址为127.0.0.1,端口号为6381,状态为在线(state=online),当前复制偏移量为294,滞后(lag)为0。

  4. master_replid:主节点启动时生成的 40 位 16 进制随机字符串,用来标识主节点。从节点连接主节点后,会将此值存储在自己的 master_replid 中。如果主节点宕机,从节点切换为新的主节点,会生成自己的 master_replid,并将之前主节点的 master_replid 存到 master_replid2 中,以便后续回复。当之前的主节点重启并成为从节点时,它的 master_replid 将是当前主节点的 master_replid
  5. master_repl_offset:复制流中的一个偏移量。主节点处理完写入命令后,会把命令的字节长度累加记录在这个字段。它是实现部分复制的关键字段,从节点收到主节点发送的命令后,也会累加自身的偏移量。通过比较主从节点的复制偏移量,可以判断主从节点数据是否一致。
  6. repl_backlog_active:表示复制积压缓冲区是否处于活动状态。
  7. repl_backlog_size:复制积压缓冲区的大小。它是一个环形缓冲区,用于存储主节点向从节点传递的命令。
  8. repl_backlog_first_byte_offset:复制积压缓冲区中第一个字节的偏移量。
  9. repl_backlog_histlen:复制积压缓冲区中当前存储的命令字节数。

  在Redis中,偏移量(offset)表示从节点在主节点上复制数据的进度。主节点持续地接收写入操作,并将这些修改同步到连接的从节点。从节点需要不断地从主节点获取新的修改,并将其应用到本地数据集上。因此,偏移量可以看作是从节点和主节点之间数据同步的进度指示器,表示从节点已经复制了主节点的数据到哪个位置。随着数据的不断写入,从节点的偏移量也会不断增加,直到它与主节点的数据完全一致

  lag 表示从节点落后于主节点的数据复制延迟。当主节点接收到写入操作时,它会将这些操作同步到连接的从节点。lag 值表示从节点当前的复制偏移与主节点的复制偏移之间的差异,这个差异代表了从节点的复制延迟。lag 值越大,表示从节点与主节点之间的数据同步越滞后,即复制延迟越严重。

  通常情况下,从节点的 lag 值应该保持在一个较低的水平,以确保数据同步的及时性和准确性。如果 lag 值持续增长,可能会导致从节点无法及时获取主节点的最新数据,从而影响系统的实时性和数据一致性。因此,在分布式系统中,及时监控 lag 值并采取必要的措施来降低复制延迟是非常重要的。

  我们再来看一下从节点:

   根据提供的信息,我们可以得出以下分析:

  • Role: Redis 的角色是从服务器 (slave),这意味着这个 Redis 实例充当了从节点的角色。
  • Master Host和Master Port: 该从节点的主节点是位于127.0.0.1地址,端口号为6379的主节点。
  • Master Link Status: 主节点的连接状态为正常(master_link_status:up),表示从节点与主节点之间的连接处于可用状态。
  • Master Last IO Seconds Ago: 从节点最近一次与主节点进行IO操作的时间为4秒,即最近一次通信是在当前时间内完成的。
  • Master Sync in Progress: 从节点当前没有进行同步操作(master_sync_in_progress:0),说明它与主节点的数据已经同步完成。
  • Slave Repl Offset: 从节点当前的复制偏移量为406,表示从节点已经成功复制了主节点的数据,且复制偏移量为406。
  • Slave Priority和Slave Read Only: 从节点的优先级为100(slave_priority:100),并且只读模式已启用(slave_read_only:1)。
  • Connected Slaves: 从节点当前没有连接其他从节点(connected_slaves:0),说明该节点没有其他从节点连接到它。
  • Master Replid和Master Repl Offset: 主节点的复制ID和复制偏移量与上一个示例中相同,表示主节点的信息保持一致。
  • Repl Backlog相关参数: 同上一个示例中相同,后备日志缓冲区已激活,大小为1MB,历史长度为406字节。

  上述信息在客户端可以详细的看出是主节点还是从节点,并且从节点数据同步到了哪里。大可不必记上述字段,可在redis官网中查看。

断开主从复制

  slaveof no one 命令用于将一个从节点(Slave)转变为独立的主节点(Master)。当在一个从节点上执行slaveof no one命令后,会发生以下情况:

  1. 该节点不再从原来的主节点接收复制数据和命令。

  2. 该节点开始独立运行,接受客户端的读写请求,并自行管理数据。

  3. 从节点提升为主节点后,它将保留在作为从节点期间接收到的数据。

  我们在从节点的客户端执行一下该命令,具体如下图:

  写入命令也是可以了:

  当然,主节点上的从节点的数量也就变成了1的,就只有6381的服务端一个从节点了,如下图:

  通过slaveof命令还可以实现切主操作,将当前从节点的数据源切换到另一个主节点。执行slaveof {newMasterlp} {newMasterPort}命令即可。 切主操作主要流程:

  • 断开与旧主节点复制关系。
  • 与新主节点建立复制关系。
  • 删除从节点当前所有数据。
  • 从新主节点进行复制操作。

  我们接着6380服务端的redis断开了主节点看,如下图:

  现在我们通过 slaveof 127.0.0.1 6381 命令,与新主节6381的Redis服务点建立复制关系。如下:

  此时6380服务的从节点为了避免数据混乱,从节点在切换主节点时需要清空当前数据,以确保从新主节点同步到的数据是最新的。建立了新的复制关系后,从节点会开始从新主节点同步数据,以保持与新主节点的数据一致性。此时数据同步过程就是由6379同步给6381,然后6381再同步给6380。具体如下图:

  需要注意的是:刚才通过slaveof是修改了主从结构,此处的修改是临时性的。如果重新启动了redis服务器,仍然会按照最初在配置文件中设置的内容来建立主从关系!

安全性

  对于数据比较重要的节点,主节点会通过设置requirepass参数进行密码验证,这时所有的客户端访问必须使用auth命令实行校验。从节点与主节点的复制连接是通过一个特殊标识的客户端来完成,因此需要配置从节点的masterauth参数与主节点密码保持一致,这样从节点才可以正确地连接到主节点并发起复制流程。 

只读

   默认情况下,从节点使用slave-read-only=yes配置为只读模式。由于复制只能从主节点到从节点,对于从节点的任何修改主节点都无法感知,修改从节点会造成主从数据不一致。所以建议线上不要修改从节点的只读模式。

传输延迟

   Redis中主节点和从节点之间通过网络来传输(TCP)。TCP中有一个Nagle,Nagle 算法的基本定义是任意时刻,最多只能有一个未被确认的小段。所谓“小段”,指的是小于 MSS(Maximum Segment Size,最大报文段长度)尺寸的数据块,而“未被确认”是指一个数据块发送出去后,没有收到对方发送的 ACK 确认报文。

  Nagle 算法的原理是:在 TCP 连接中,任意时刻只能有一个未被确认的小片段。在发送出去的报文中,必须要等待对方发送 ACK 之后,服务端才会发送一个新的报文。当发送完一个小分组后,需要一直等待该分组的确认 ACK 到达,否则不会发送其他分组。当确认到达之后,TCP 会收集已经准备好的小分组,并将它们合并成一个大的分组发送出去,从而减少了网络拥塞的可能性,降低了网络延迟,并提高了吞吐量

  Nagle 算法的优点是可以减少网络中充斥的小数据块,提高网络带宽的利用率,降低网络拥塞的可能性。然而,它也存在一些缺点,例如严重影响请求响应式协议的延迟,对于实时性要求很高的交互,不适合使用 Nagle 算法。

  另外,Nagle 算法与延迟 ACK 机制也有关联。延迟 ACK 指接收端等待延时 ACK 计时器后统一对接收到的报文进行 ACK,而非每个报文都立即 ACK。当客户端发送消息给服务器时,如果客户端收到 ACK 后等待延时 ACK 计时器结束才进行延迟应答,服务器由于未收到对方的 ACK 而一直等待,可能导致死锁,此时只有等待延时计时器结束(至少 40ms)才能解决死锁问题。 

  Redis 中就禁止了 Nagle 算法,以避免延迟,因为 Redis 的命令都要求尽快传输到 Redis 服务器,而不需要等待其他命令或缓冲区填充。

  主从节点一般部署在不同机器上,复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP_NODELAY,默认为no,即开启tcp-nodelay功能,说明如下:

  • 当关闭时,主节点产生的命令数据无论大小都会及时地发送给从节点,这样主从之间延迟会变小,但增加了网络带宽的消耗。适用于主从之间的网络环境良好的场景,如同机房部署。
  • 当开启时,主节点会合并较小的TCP数据包从而节省带宽。默认发送时间间隔取决于Linux的内核,一般默认为40毫秒。这种配置节省了带宽但增大主从之间的延迟。适用于主从网络环境复杂的场景,如跨机房部署。

三、拓扑结构

  Redis的复制拓扑结构可以支持单层或多层复制关系,简单理解拓扑结构:若干个节点之间,按照啥样的方式来进行组织连接。根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构。 

  1. 一主一从结构:这是最简单的复制拓扑结构,其中一个主节点负责处理写操作和读操作,而一个从节点复制主节点的数据,并只负责处理读操作。这种结构主要用于主节点故障转移,当主节点的写命令并发高且需要持久化时,可以只在从节点开启 AOF(主节点不需要),这样既保证了数据的安全性,也避免了持久化对主节点的性能干扰。但需注意,当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作。因为主节点之前没有开启持久化功能,自动重启后数据集为空,这时从节点如果继续复制主节点会导致从节点数据也被清空,丧失了持久化的意义。安全的做法是在从节点上执行 slaveof no one 断开与主节点的复制关系,再重启主节点从而避免这一问题。
  2. 一主多从结构(星形拓扑结构):在这种结构中,一个主节点可以有多个从节点。多个从节点可以分担读操作的负载,适用于读操作较多的场景,能把读命令发送到从节点来分担主节点压力。日常开发中如果需要执行一些比较耗时的读命令(如 keyssort 等),可以在其中一台从节点上执行,防止慢查询对主节点造成阻塞从而影响线上服务的稳定性。然而,对于写并发量较高的场景,多个从节点会导致主节点写命令的多次发送,过度消耗网络带宽,同时也加重了主节点的负载,影响服务稳定性。
  3. 树状主从结构(树状拓扑结构):这种结构是对一主多从的补充,从节点不但可以复制主节点数据,还可以作为其他从节点的主节点继续向下层复制。通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量。数据写入主节点后会同步到第一层的从节点(如节点 b 和 c),第一层的从节点再把数据同步到下一层的从节点(如节点 d 和 e),实现数据的逐层复制。当主节点需要挂载多个从节点时,为避免对主节点的性能干扰,可以采用这种树状主从结构来降低主节点压力。

  接下来再对上述结构描述做出简单解释。

  一主一从:如果写数据请求太多,此时也是会给主节点造成一些压力。可以通过关闭主节点的AOF,只在从节点上开启AOF。这样就避免了主节点的写盘操作,从而减小主节点的压力。但是这种设定方式,有一个严重的缺陷:主节点一旦挂了,不能让他自动重启(如果自动重启,此时没有AOF文件就会丢失数据,进一步的主从同步,会把从节点的数据也给删了)。改进办法:当主节点挂了之后,就需要让主节点从从节点这里获取到AOF的文件再启动。

  一主多从: 实际开发中,读请求远远超过写请求。主节点上的数据发生改变,就会把改变的数据同时同步给所有的从节点。随着从节点个数的增加,同步一条数据就需要传输多次。这时候主节点的压力就会较大。尤其是在对主节点写操作较多时,还是需要考虑到主节点很有可能就会成为性能瓶颈!

  树形主从:针对一主多从的结构的缺点,引出了树形主从结构。在树形主从结构中,一旦数据进行修改,主节点的压力会变小很多,性能干扰就会很小。但是从节点同步延时往往会变得更长。这是因为在这种结构中,数据的变动可能需要通过多个层级传递和更新,从而增加了同步所需的时间。特别是在较大的树形结构中,数据的传播和更新可能需要经过多个节点,每个节点都需要相应的处理和确认,这就导致了同步延迟的增加。此外,如果网络连接或者节点之间的通信出现问题,也会进一步延长同步的时间。因此,在树形主从结构中,及时考虑和优化同步机制是非常重要的,以确保数据的一致性和及时性。

  总上,在不同的场景中可根据业务需求来选择不同的拓扑结构。 

四、主从复制原理

主从节点建立复制流程图

   流程图如下:

  现在我们来理解一下上述的流程图:

  • 保存主节点信息: 在配置主从同步关系之前,从节点只保存主节点的地址信息。此时建立复制流程还没有开始,在从节点6380执行info replication可以看到如下信息: 
    这通常包括主节点的 IP 地址和端口号。此时,主节点的连接状态被标记为下线(down)。
  • 主从建立连接:从节点内部通过每秒运行的定时任务维护复制相关逻辑。当定时任务发现存在新的主节点后,该定时任务会尝试与主节点建立TCP 的网络连接。如果连接失败,则定时任务会无限重试直到连接成功或者管理员手动停止主从复制。TCP三次握手是为了验证通信双方是否能正确读写数据。
  • 发送 Ping 命令: 如果连接建立成功,从节点会发送 Ping 命令给主节点,以确认主节点在应用层上正常工作。主节点在收到 Ping 命令后,会回复 Pong,表示主节点正常运行。如果 Ping 命令的回复超时或者出现其他异常,从节点会断开 TCP 连接,并在下一次定时任务中再次尝试连接。
  • 权限验证: 如果主节点设置了密码(requirepass 参数),则从节点需要进行密码验证。从节点在连接主节点时会使用配置的 masterauth 参数来传递密码信息。如果密码验证失败,则从节点的复制过程将被终止。
  • 同步数据集。对于首次建立复制的场景,主节点会把当前持有的所有数据全部发送给从节点,这步操作基本是耗时最长的,所以又划分称两种情况:全量同步和部分同步,下文会详细介绍
  • 命令持续复制。当从节点复制了主节点的所有数据之后,针对之后的修改命令,主节点会持续的把命令发送给从节点,从节点执行修改命令,保证主从数据的一致性。

数据同步psync 

  Redis使用psync命令完成主从数据同步,同步过程分为:全量复制和部分复制。psync不需要咱们手动执行,redis服务器会在建立好主从同步关系之后,自动执行psync。注意:从节点负责执行psync,从节点从主节点这边拉取数据。

  Redis使用psync命令完成主从数据同步,其中同步过程分为全量复制和部分复制两种方式:

  • 全量复制: 一般用于首次建立主从复制关系的场景。在全量复制中,主节点会将全部数据一次性发送给从节点,以确保从节点与主节点的数据完全一致。虽然全量复制能够保证数据的完整性,但当数据量较大时,会对主节点、从节点和网络造成较大的负担和开销。因此,全量复制通常适用于初始复制的场景,而不适用于频繁的数据同步。
  • 部分复制:用于处理在主从复制中由于网络闪断等原因导致的数据丢失场景。当从节点再次连接上主节点后,如果条件允许,主节点会补发丢失的数据给从节点。部分复制的特点是只传输丢失的数据,因此补发的数据量远远小于全量数据,可以有效避免全量复制带来的高开销。部分复制能够提高数据同步的效率和速度,减少了对网络带宽的占用和对系统资源的消耗。

  psync命令语法格式:

PSYNC replicationid offset

  首先看一下 replicationid。在 Redis 中,replicationid(简称 replid)是数据集的标记。每一个 Redis 主节点都有唯一的 replicationid ,而从节点则会复制并保存主节点的 replicationid 。

  replicationid 的主要作用是用于判断主从节点之间数据的一致性和同步状态。当从节点与主节点进行数据同步时,主节点会根据从节点发送过来的 replicationid 来判断是否为第一次同步。

  如果从节点发送的 replicationid 为-1,则认为是第一次同步;如果一致,则认为之前已经同步过了。在第一次同步时,主节点会把自己的 replicationid 发送给从节点,从节点记录下来,在后面的同步过程中使用。

  例如,当从节点重新连接到主节点或者网络连接中断后恢复时,它会向主节点发送自己记录的 replicationid 和 offset(偏移量)。主节点根据这些信息来确定从节点需要同步哪些数据。如果 offset 小于主节点的当前 offset,说明从节点的数据落后于主节点,需要进行数据更新。

  我们再来看一下 offset。参与复制的主从节点都会维护自身复制偏移量。主节点(master)在处理完写入命令后,会把命令的字节长度做累加记录,统计信息在info replication中的master_repl_offset指标中。

  从节点(slave)每秒钟上报自身的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:

  从节点在接受到主节点发送的命令后,也会累加记录自身的偏移量。统计信息在info replication中的slave_repl_offset 指标中:

  复制偏移量的维护如图所示。通过对比主从节点的复制偏移量,可以判断主从节点数据是否一致。

    通过 replicationid 和 offset 的配合,两者共同标示了一个数据集合,Redis 可以实现主从数据的高效同步,包括全量同步(第一次同步或无法进行部分同步时)和增量同步(网络连接中断后恢复时的部分数据同步)。这样可以确保从节点的数据与主节点保持一致,并且在网络异常等情况下能够进行断点续传,提高了 Redis 主从复制的可靠性和稳定性。

  细心的同学可能发现了,为什么每个节点都会有两个master_replid呢? master_replid是用来标识主节点的唯一标识符,每当主节点发生变化,例如主节点重启或者主节点发生切换(比如从节点晋升为主节点),都会生成一个新的master_replid。这个master_replid对于从节点来说是非常重要的,因为它帮助从节点识别和连接正确的主节点。

  现在假设有两个节点,A是主节点,B是从节点。B记录了A的master_replid。但如果网络发生抖动,B可能会误以为A挂掉了,并尝试将自己晋升为主节点,于是为自己分配了一个新的master_replid1,并且记录旧的replid放到了master_replid2中。这样一来,B就有了两个master_replid,一个是之前A的,另一个是自己的。

  这个备用master_replid2在后续网络恢复后非常有用。即使网络重新连接后,B仍然可以根据master_replid2找回之前的主节点,继续进行数据同步。如果网络没有恢复,B则会按照新的master_replid1自成一派,继续处理后续的数据。

  通过这样的设置,即使发生了网络中断或主节点切换,从节点仍然能够保持与正确的主节点的连接,从而保障了主从复制过程的可靠性和稳定性。这种备用master_replid的设计确保了主从复制系统能够在不可预测的情况下正常运行,从而提高了系统的可用性和容错能力。

  psync同步过程分为全量复制和部分复制,那么该命令怎么触发这两种复制呢?

  • 由于是第一次进行复制,从节点没有复制偏移量和主节点的 replicationid。如果将replicationid设为"?",并且将offset设为-1,那么就是在尝试进行全量复制。
  • 如果将replicationid设为具体的数值,并且将offset设为某个特定的数值,那么就是在尝试进行部分复制。在这种情况下,从节点会向主节点发送psync命令,请求从指定的复制ID和偏移量处开始的数据同步。

  如果两个节点他们的replid和 offset都相同,则这两个节点上持有的数据就一定相同。

psync运行流程 

  再次说明,是从节点发起psync命令,然后主节点同步/复制数据给从节点,并不是主节点主动去同步数据的。具体流程如下图:

  简单解释一下上图流程:

  • 从节点发送psync命令给主节点,replid和offset的默认值分别是?和-1;
  • 主节点根据psync参数和自身数据情况决定响应结果;
  • 如果回复+FULLRESYNC replid offset,则从节点收到这个回复后,会启动全量复制流程,从主节点重新获取所有数据。
  • 如果回复+CONTINEU,从节点收到这个回复后,会根据主节点发送的增量数据进行同步。
  • 如果回复-ERR,说明Redis主节点版本过低,不支持psync命令。从节点可以使用sync命令进行全量复制。

  我们可能会看到全量同步和部分同步,与全量复制和部分复制的两组概念。“全量复制”和“部分复制”与“全量同步”和“部分同步”在概念上是相似的:

  • “全量复制”/“全量同步”都是指将主节点的全部数据完整地复制/同步到从节点。通常发生在初次建立主从关系,或者因为某些原因(如复制连接完全断开有大量数据未同步造成无法进行部分复制等)导致需要完整地重新构建从节点数据的场景。
  • “部分复制”/“部分同步”都是指主节点和从节点在已有复制/同步基础上,仅对主节点新产生的、从节点尚未拥有的数据进行传递和更新,不需要完整地重新传输所有数据。都是用于在主从连接中断后,在一定条件下进行数据补充以恢复主从数据一致性的操作。

全量复制流程

  全量复制是Redis最早支持的复制方式,也是主从第一次建立复制时必须经历的阶段。全量复制的运行流程如图所示。

  以下是对全量复制流程的详细解释:

  1. 从节点发送 psync 命令给主节点,由于是第一次复制,从节点还没有主节点的复制 ID 和复制偏移量,因此发送 psync ? -1。
  2. 主节点接收到 psync 命令后,解析命令并发现需要进行全量复制,于是回复给从节点 +FULLRESYNC 响应,并且把replid 和 offset一同发送个了从节点。
  3. 从节点接收到主节点的运行信息后进行保存,以备后续使用。
  4. 主节点执行 bgsave 命令进行 RDB 文件(二进制文件)的持久化,将当前内存中的数据保存到磁盘上。这一步骤可以确保主节点在发送数据给从节点时不会丢失数据。这里不能使用已有的RDB文件,而是必须重新生成,因为已有的RDB文件可能会和当前的最新数据存在较大差异。
  5. 主节点将生成的 RDB 文件发送给从节点,从节点接收并保存 RDB 数据到本地硬盘。
  6. 在主节点生成RDB文件和传输rdb文件的过程中,主节点还会继续收到很多新的修改操作。新修改的数据也必须要同步给从节点!主节点将从生成RDB到从节点接收完成期间执行的写命令,写入缓冲区中,等从节点保存完RDB文件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照RDB的二进制格式追加写入到收到的RDB文件中,保持主从一致性。
  7. 从节点在接收到主节点发送的所有数据后,会清空自身原有的旧数据。
  8. 从节点加载 RDB 文件,从而获取与主节点一致的数据。
  9. 如果从节点在加载完整的 RDB 文件后开启了 AOF 持久化功能,它会执行 bgrewrite 操作,生成最新的 AOF 文件。由于当前收到的是大批量的数据,AOF日志可能会包含大量的冗余信息,这可能会导致AOF文件过大,影响Redis服务器性能和可用磁盘空间。因此,对AOF日志进行整理和优化是非常必要的。

  通过分析全量复制的所有流程,我们会发现全量复制是一件高成本的操作:主节点bgsave的时间,RDB 在网络传输的时间,从节点清空旧数据的时间,从节点加载RDB的时间等。所以一般应该尽可能避免对已经有大量数据集的 Redis进行全量复制。

  在上述了全量复制的流程中,我们发现网络传输的是rdb文件。那么能不能把快照生成的二进制rdb数据不写入rdb文件,而是直接通过网络传输同步给从节点呢?从节点这样也就不用在收到rdb文件后进行对文件的读写操作了。答案是可以的!上述方式成为无磁盘复制(diskless replication)。

  磁盘复制和无磁盘复制(diskless replication)是在Redis中进行全量复制时两种不同的方式。

  1. 磁盘复制(Disk-based Replication):在磁盘复制中,默认情况下,主节点在进行全量复制时会先将当前内存中的数据集进行持久化,即生成一个RDB文件,并将该文件保存到主节点的磁盘中。然后,主节点会通过网络将生成的RDB文件发送给从节点,从节点接收到RDB文件后进行加载,从而获取与主节点一致的数据集。
  2. 无磁盘复制(Diskless Replication):自Redis 2.8.18版本开始,Redis引入了无磁盘复制的特性。在无磁盘复制中,主节点在执行RDB生成过程时不会将RDB文件写入磁盘,而是直接将生成的RDB数据通过网络发送给从节点。这样做可以避免了磁盘读写操作,从而提高了复制的效率和速度,减少了对硬盘IO的负载,特别是对于大规模数据集的情况下,无磁盘复制可以显著提升全量复制的性能。

  即使引入了无硬盘模式,仍然整个操作是比较重量,比较耗时的。网络传输是没法省的!相比于网络传输来说,读写硬盘是小头。而全量复制网络传输大规模数据是最耗时的。

部分复制流程

  从节点要从主节点这里进行全量复制,而全量复制开销是很大的。有些时候从节点本身已经持有了主节点的绝大部分数据,这个时候就不太需要进行全量复制了。

  比如出现网络抖动,主节点这边最近修改的数据可能就无法及时同步过来了。网络抖动一般都是"暂时的",过一会就恢复了。更严重的从节点已经感知不到主节点了(进一步的从节点可能会升级成主节点)。当网络状况恢复时,此时就可以让从节点和主节点重新建立联系。当从节点和主节点重新建立连接之后,就需要进行数据的同步。此时同步的数据并不需要全量复制,根据从节点的psync replicationid offset进行部分复制即可!

  我们来看一下部分复制的流程,如下图:

  以下是对上述流程的详细解释:

  1. 当主从节点之间发生网络中断时,主节点启动一个计时器,计时器的时长由配置的repl-timeout参数决定。若在设定的时间内未收到从节点的响应,主节点将判定从节点故障,并终止复制连接,以确保系统的稳定性和数据的一致性。
  2. 在主从连接中断期间,尽管主节点可以继续接收客户端发送的命令,但由于复制通道中断,主节点产生的复制命令无法即时发送给从节点。这些待发送的复制命令被暂存在复制积压缓冲区中,等待后续从节点重新连接并请求复制数据时发送给它们,以保证主从节点间数据同步不会中断。
  3. 一旦主从节点的网络连接恢复,从节点会重新尝试连接到主节点,以继续进行复制过程。
  4. 从节点重新连接后,会将之前保存的 replication ID 和复制偏移量作为psync命令的参数发送给主节点,请求进行部分复制。这些参数用于确定从节点需要同步的数据的起始位置。
  5. 主节点收到psync请求后,首先进行验证以确保主从节点的复制状态一致。然后,主节点根据从节点提供的偏移量在复制积压缓冲区中查找相应的数据。若在缓冲区中找到匹配的数据,则向从节点发送+CONTINUE响应,表示准备好了要发送的数据。
  6. 主节点随后将需要同步给从节点的数据发送给从节点,完成主从节点之间的数据一致性。这样,即使发生了网络中断,部分复制机制也能够确保主从节点之间的数据同步,减少了因全量复制而带来的高成本和性能开销。

  复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,当主节点有连接的从节点(slave)时被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区,如图所示。 

  由于缓冲区本质上是先进先出的定长队列,所以能实现保存最近已复制数据的功能,用于部分复制和复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的info replication中:

  其中,repl_backlog_active 表示复制缓冲区是否启用,repl_backlog_size 表示缓冲区的最大长度,repl_backlog_first_byte_offset 表示缓冲区的起始偏移量,而 repl_backlog_histlen 表示已保存数据的有效长度。通过这些统计指标,我们可以计算出复制积压缓冲区内的可用偏移量范围,即从 repl_backlog_first_byte_offset 开始,到 repl_backlog_first_byte_offset + repl_backlog_histlen 结束,这个范围内的值相当于环形队列中的数组下标。

  replicationld其实就是在描述"数据的来源",offset描述"数据的复制的进度"。如果replicationld一样,再来判定offset。offset表示从节点之前同步数据的进度是如何。主节点就看这个进度是否在当前的积压缓冲区之内,如果确实是在积压缓冲区之内,此时就可以直接进行部分复制。就只把最近这段时间的数据给复制过去即可。如果确实当前从节点的进度已经超出积压缓冲区的范围了,那就说明有太多的数据未同步了,此时只能进行全量同步了。

实时复制

  主从节点在建立复制连接后,主节点会把自己收到的修改操作通过 tcp长连接的方式,源源不断的传输给从节点。从节点就会根据这些请求来同时修改自身的数据。从而保持和主节点数据的一致性。在这个流程中,延时是一个重要的因素。一般情况下,实时同步的延时较短,即从主节点接收到修改请求到从节点完成数据更新的时间较短。然而,在存在多级从节点的树形结构中,延时可能会增加。

  另外这样的长连接,需要通过心跳包的方式来维护连接状态。(这里的心跳是指应用层自己实现的心跳,而不是TCP自带的心跳)。

  1. 主从节点之间通过模拟对方的客户端进行通信,定期发送心跳消息或信号来确认连接的健康状态。
  2. 主节点默认每隔一定时间(通常为10秒)向从节点发送 ping 命令,以检测从节点的存活性和连接状态。如果从节点未能及时响应 ping 命令,主节点可以根据具体情况采取相应的措施,例如重新连接或标记从节点为下线状态。
  3. 从节点会周期性地(通常为每隔1秒)向主节点发送 replconf ack {offset} 命令,用于向主节点报告自己当前的复制偏移量。通过这个命令,主节点可以了解从节点的复制进度,从而及时监控复制的状态和进程。
  4. 如果主节点在设定的 repl-timeout 时间内未收到从节点的响应,即从节点的通信延迟超过了配置的阈值(默认为60秒),主节点将判定从节点为下线状态,并断开与其的复制客户端连接。一旦从节点恢复连接,心跳机制会重新启动,确保主从节点之间的连接状态和数据同步的持续性。

五、总结

主从复制解决的问题

  主从复制是一种用于解决单点问题的重要技术,主要针对单个 Redis 节点的可用性和性能有限等问题。

  1. 提高可用性:单个 Redis 节点可能会面临因硬件故障、网络问题或软件错误等原因导致的宕机风险。通过主从复制,可以创建多个从节点,当主节点出现故障时,可以快速切换到其中一个从节点,保证服务的持续可用性。这种故障转移的能力大大提高了系统的稳定性和可靠性。
  2. 提升性能:单个 Redis 节点的性能有限,无法满足高并发和大规模数据处理的需求。通过主从复制,可以将读请求分发到多个从节点上进行处理,从而分担主节点的负载,提高整体系统的读取性能和并发处理能力。此外,通过水平扩展和负载均衡等技术,可以进一步提升系统的性能。

  总的来说,主从复制技术通过创建多个节点并建立节点之间的复制关系,有效地解决了单点故障和性能瓶颈等问题,提高了 Redis 系统的稳定性、可用性和性能。

主从复制的特点

主从复制是 Redis 中一项重要的功能,具有以下特点:

  • 创建主节点的多个副本:通过复制功能,主节点可以拥有多个从节点副本,这些从节点将复制主节点的数据,实现数据的备份和冗余,提高系统的可靠性和容错性。
  • 读写分离:主节点负责处理写入操作,而从节点用于处理读取请求。通过读写分离,可以有效降低主节点的访问压力,提高系统的读取性能和并发处理能力。
  • 多种拓扑结构:复制支持多种拓扑结构,例如单主单从、主从链、主从树等,可以根据不同的场景和需求选择合适的拓扑结构,灵活应对各种复制架构需求。
  • 多种复制方式:复制过程包括全量复制、部分复制和实时复制。全量复制用于初次复制场景,部分复制用于处理因网络闪断等原因造成的数据丢失场景,而实时复制则通过长连接的方式持续传输修改操作,保持主从节点的数据实时同步。
  • 心跳机制:主从节点之间通过心跳机制保证通信的正常和数据的一致性。主节点定期发送 ping 命令检测从节点的存活性,从节点定期向主节点报告复制偏移量,以确保复制连接的稳定性和数据的同步性。

主从复制的优缺点

  主从复制在提高系统的可用性和可靠性方面有诸多优势,但也存在一些局限性和缺点。

优点:

  1. 负载分担和读写分离:主从复制可以将读请求分发到多个从节点上,减轻了主节点的负载压力,提高了系统的读取性能和并发处理能力。
  2. 数据冗余和备份:通过从节点的存在,可以实现数据的备份和冗余,提高了系统的可靠性和容错性,当主节点发生故障时,可以快速切换到从节点继续提供服务。
  3. 故障恢复:在主节点故障时,可以通过从节点进行故障恢复,提高了系统的可用性,降低了服务中断的风险。
  4. 扩展性:可以通过添加更多的从节点来扩展系统的读取能力,从而满足系统的扩展需求。

缺点:

  1. 复制延迟:当从节点数量增多时,复制数据的延迟会变得明显,可能导致从节点上的数据与主节点数据之间存在一定程度的时间差。
  2. 故障转移需要手动干预:在主节点发生故障时,从节点不会自动晋升为主节点,需要管理员手动介入,进行故障恢复和主从切换,增加了管理和维护的复杂性。
  3. 网络带宽压力:主从复制会占用一定的网络带宽资源,特别是在复制大量数据时,可能会给网络带宽造成一定压力,影响其他系统的正常运行。
  4. 一致性保障:在主从复制过程中,由于网络延迟或其他原因,可能会导致主从节点之间的数据不一致,需要额外的机制来保障数据的一致性和完整性。

参考文章:​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、数据同步psync)、总结_redis psync-CSDN博客 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/381365.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java开发之Java容器

#来自ゾフィー(佐菲) 1 总览 1.1 List ArrayList: Object[]数组Vector:Object[]数组LinkedList: 双向链表,JDK1.6 之前为循环链表,JDK1.7 取消了循环 1.2 Set HashSet:无序&#xf…

mybatis 报CannotGetJdbcConnectionException

目录 报错起因 报错截图 运行环境 数据库配置 解决思路 报错起因 在web项目上拉取代码启动web服务抛CannotGetJdbcConnectionException。 报错截图 运行环境 windows idea maven tomcat springMVC mybatis 数据库配置 urlxxx driverClassNamexxx usernamexxx pass…

docker compose 容器 编排分组

遇到问题:执行docker compose up -d 后docker compose 创建的容器们 在desktop-docker 中都在docker下一堆 搜索想着能不能把这个docker名字改一下,但是都没有找到这样的一个方案; 最后发现,我执行docker compose up -d 命令所在…

【数据结构】二叉树OJ题_对称二叉树_另一棵的子树

对称二叉树 题目 101. 对称二叉树 - 力扣(LeetCode) 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true示例 2: 输入:root [1,2…

Linux文件和目录常用命令

1.操作命令 查看目录内容 ls 切换目录 cd 创建和删除操作 touch rm mkdir 拷贝和移动文件 cp mv 查看文件内容 cat more grep 其他 echo 重定向 > 和 >> 管道 | 1.1 终端实用技巧 1>自动补全 在敲出 文件/目录/命令 的前几个字母之后,按下…

git实操之线上分支合并

线上分支合并 【 1 】本地dev分支合并到本地master上 # 本地dev分支合并到本地master上# 远程(线上)分支合并# 本地dev分支合并到本地master上# 远程(线上)分支合并#####本地和线上分支同步################ #### 远程创建分支,拉取到本地####-远程创建分支&#…

FPGA:频闪灯设计

1、需求 若在FPGA上实现LED灯一秒闪烁一次,先进行计算,1秒闪烁一次,即周期为1秒,开发板XC7A35TFFG-2的基本时钟输入由板载 50MHz 有源晶振提供,即频率为f 50MHz 。 则一个周期为 T 1 f 1 50 M H z 20 n s T\frac{…

git使用、git与idea结合、gitee、gitlab

本文章基于黑马程序javase模块中的"git"部分 先言:git在集成idea中,不同版本的idea中页面显示不同,操作时更注重基于选项的文字;git基于命令操作参考文档实现即可,idea工具继承使用重点掌握 1.git概述 git是目前世界上最先进的分布式文件版本控制系统 分布式:将…

FastAPI(六十六)实战开发《在线课程学习系统》接口开发--用户注册接口开发

在前面我们分析了接口的设计,那么我们接下来做接口的开发。 首先,我们先设计下pydantic用户参数的校验: """ -*- encodingutf-8 -*- Time: 2024/7/19 16:48 Author: lc Email: 15101006331163.com File: schemas.py "&…

基于单片机的智能医疗监护系统设计

1.简介 随着社会的发展,智能化电子设备成为了人们生活中不可或缺的一部分,尤其是在人们对于身心健康更加注重的今天,智能医疗监护系统应运而生。本套电子监护设备集体温测量、心电采集、心率监测、血氧监测于一体,带有语音播报模块…

Thinkphp开发文档二次整理版

基础部分 安装 环境要求 ​ *php>7.1.0 命令下载 通过Composer进行下载,操作步骤下载软件 phpstudy --->点击软件管理 --->安装Composer --->再点击网站 --->点击管理 --->点击Composer --->复制如下命令代码: ​ 稳定版&…

甄选范文“论面向方面的编程技术及其应”,软考高级论文,系统架构设计师论文

论文真题 针对应用开发所面临的规模不断扩大、复杂度不断提升的问题,面向方面的编程(Aspect Oriented Programming,AOP)技术提供了一种有效的程序开发方法。为了理解和完成一个复杂的程序,通常要把程序进行功能划分和封装。一般系统中的某些通用功能,如安全性、持续性、日…

构建高效Node.js中间层:探索请求合并转发的艺术

🎉 博客主页:【剑九 六千里-CSDN博客】 🎨 上一篇文章:【CSS盒模型:掌握网页布局的核心】 🎠 系列专栏:【面试题-八股系列】 💖 感谢大家点赞👍收藏⭐评论✍ 引言&#x…

Java-Stream流

流 不同的数据有不同的方式得到其stream 单列集合&#xff1a;使用Collection中的默认方法&#xff1a;default Stream<E> stream双列集合&#xff1a;没有直接获取stream的方法&#xff0c;只能把他转化为单列集合数组&#xff1a;Arrays中的静态方法&#xff1a;publ…

SpringCloud的认识和初步搭建

目录 一.认识SpringCloud 二.SpringCloud的部署 2.1开发环境 2.2数据库的建立 2.3SpringCloud的部署 第一步&#xff1a; 创建Maven项目 第二步&#xff1a;完善pom文件 第三步&#xff1a;创建两个子项目 第四步&#xff1a;声明项目依赖以及构建插件 第五步&#xf…

NVIDIA Container Toolkit 安装与配置帮助文档(Ubuntu,Docker)

NVIDIA Container Toolkit 安装与配置帮助文档(Ubuntu,Docker) 本文档详细介绍了在 Ubuntu Server 22.04 上使用 Docker 安装和配置 NVIDIA Container Toolkit 的过程。 概述 NVIDIA 容器工具包使用户能够构建和运行 GPU 加速容器。即可以在容器中使用NVIDIA显卡。 架构图如…

JVM基本知识——运行空间

JVM&#xff08;Java Virtual Machine&#xff09;即Java虚拟机&#xff0c;是负责读取java字节码&#xff0c;并在实际的硬件环境中运行。 JVM可以分为三部分&#xff1a;类装载器&#xff08;ClassLoader&#xff09;子系统、内存空间、执行引擎 内存空间&#xff08;运行时…

防火墙--双机热备

目录 双击热备作用 防火墙和路由器备份不同之处 如何连线 双机 热备 冷备 VRRP VGMP&#xff08;华为私有协议&#xff09; 场景解释 VGMP作用过程 主备的形成场景 接口故障的切换场景 整机故障 原主设备故障恢复的场景 如果没有开启抢占 如果开启了抢占 负载分…

Debian Linux下rclone挂载谷歌云盘碰到的坑

可能是明月好久没有使用境外服务器挂载境外的云盘缘故吧,今天一个代维客户需要他的Linux服务器挂载谷歌云盘好进行云备份,本来是个很简单的事儿,没想到在rclone连接谷歌云盘的时候卡壳了,可是把明月给难为坏了,搜索到的简体中文教程倒是很多,但没有一个提到这个“坑”,最…

MySQL学习记录 —— 이십삼 MySQL服务器文件系统(3)

文章目录 1、数据字典2、系统表各种系统表 Mysql Schema是⼀个系统库&#xff0c;表中存储了MySQL服务器运行时所需的信息。广义上&#xff0c;mysql schema包含存储MySQL程序基本数据的数据字典和用于其他操作目的的系统表。数据字典表和系统表位于数据目录下一个名为mysql.ib…