Redis主从复制的集群模式

Redis主从复制的集群模式

主从复制

主从复制,指将一台 Redis 服务器的数据,复制到其他的 Redis 服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

Redis 引入主从复制功能有几个重要原因:

  1. 数据备份和高可用性: 主从复制允许在不影响主服务器性能的情况下创建一个或多个从服务器,从而将主服务器上的数据复制到从服务器。这样可以实现数据备份,同时在主服务器发生故障时,从服务器可以接管,提高系统的可用性。
  2. 读写分离:主从复制使得可以将读和写操作分别分配给主服务器和从服务器。主服务器负责处理写操作,而从服务器可以负责处理读操作,从而分担了主服务器的负担,提高了系统的性能和扩展性。
  3. 灾难恢复: 在发生灾难性事件时,如果主服务器数据丢失或不可用,可以使用从服务器进行数据恢复。这有助于减小数据丢失的风险,提高系统的可靠性。
  4. 扩展性: 通过将负载分布到多个服务器上,主从复制提高了系统的扩展性。可以通过添加更多的从服务器来应对不断增长的负载,而无需对主服务器进行修改。
  5. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写 Redis 数据时应用连接主节点,读 Redis 数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高 Redis 服务器的并发量。

主从配置

启动一个主节点

redis-server -p 6379

启动一个从节点以并配置其作为主节点的从节点

redis-server --port 6380 --slaveof 127.0.0.1 6379

然后启动两台节点的客户端

redis-cli -p 6379
redis-cli -p 6380

使用下面的命令从两个实例中获取 Replication 节的相关信息。

INFO replication

3f0246a093a5

然后在主节点设置一个键,在从节点是可读的。从节点是只读的,如果在从节点修改键值会报错

66557820ed0d

可以通过配置 slave-read-only 为 no 以使从节点可写,但是这没什么意义,因为写入从节点不会复制到集群中的任何节点,而且会被主节点的下一次修改覆盖。

复制原理

Redis 的主从复制是通过一种异步复制的机制来实现的,基本流程:

  1. 触发复制: 主从复制的过程通常由从服务器发起。从服务器连接到主服务器并发送 SYNC命令,请求进行一次完整的同步。
  2. 主服务器生成快照: 主服务器在接收到 SYNC命令后,开始生成一个快照(RDB 文件)并将该快照保存到磁盘。同时,主服务器将新的写命令(写操作)缓存到内存缓冲区。
  3. 复制初始化: 一旦快照生成完成,主服务器将快照文件传送给从服务器。从服务器接收并加载这个快照文件,将自己的数据库状态更新为主服务器的当前状态。之后,主服务器将缓冲区中的写命令发送给从服务器,从服务器执行这些写命令,以保持与主服务器的数据同步。
  4. 心跳检测: 主从复制过程中,主服务器和从服务器之间会保持心跳检测的连接。如果从服务器在一定时间内没有收到主服务器的心跳或数据包,它将尝试重新连接或请求重新同步。
  5. 持续同步: 一旦完成初始同步,主从服务器之间将建立一个持续同步的连接。主服务器的写操作将异步传播到所有连接的从服务器,以确保主从服务器之间的数据保持一致。

总体而言,主从复制是通过快照和增量复制的结合来实现的。这种异步复制机制使得从服务器可以在主服务器的基础上保持实时同步,提高了系统的可用性和可靠性。

使用 netcat 模拟从节点与主节点进行通信。向主节点发送一个 SYNC 开始同步,主节点返回快照文件和缓存的命令,

5aa95f27c9a4

从节点之后会将收到的内容写入硬盘中的临时文件中并替换目录下的 RDB 快照文件,之后的过程就是 redis 持久化恢复的过程了。最后将主节点缓冲区中的写命令发送给从服务器完成复制初始化。

redis 在同步的过程中不会阻塞,可以继续处理来自客户端的请求,但是会使用同步前的数据进行响应。可以配置 slave-serve-stable-data 参数为 no 来使数据库在同步完成前对所有命令都回复错误(除了 INFO 和 SLAVEOF)。

完成复制初始化后,进入持续同步阶段,主节点中任何导致数据变化的命令都会发送到从节点。使用 RESP 协议进行通信。在主节点执行命令 SET key1 value,在模拟从节点的 nc 客户端可以收到如下格式的消息。

181a6349fa67

在没有增量复制时,主服务器和从服务器之间会保持心跳检测的连接。如果从服务器在一定时间内没有收到主服务器的心跳或数据包,它将尝试重新连接或请求重新同步。可以看到 nc 客户端每隔一段时间收到了来自主节点的 ping

e7e660710c22

redis 采取了乐观复制的策略,即容忍在一段时间内主从节点的数据是不同步的,但是两者最终都会同步。也就是说,redis 的主从节点的复制过程本身是异步的,这一特性保证了主节点的性能。

但是,当主节点将新的命令传递给从节点之前,双方的网络连接断开了,此时二者的数据就会是不一致的。从这个角度来看,主节点是无法知道这个命令的结果是最终同步给了多少个数据库的,这样可能降低数据的冗余存储程度。

不过 redis 提供了两项配置。

  1. min-slave-to-write

    • 这个配置项规定了在主服务器可以执行写操作之前,至少需要有多少个从服务器在线且可用。如果配置为一个正整数 N,那么只有当有至少 N 个从服务器在线时,主服务器才能执行写操作。如果从服务器的数量少于 N,主服务器将拒绝写操作。
    • 这个配置项的目的是为了确保在进行写操作时,至少有足够数量的从服务器可用,以提高系统的可用性和冗余度。

    例如:

    min-slave-to-write 3
  2. min-slave-max-lag

    • 这个配置项规定了从服务器允许的最大延迟时间。如果一个从服务器的复制延迟超过这个配置的值,主服务器将认为该从服务器不可用,不再将写操作传播给它。
    • 这个配置项的目的是确保从服务器的状态与主服务器保持相对实时,避免延迟过大导致的数据不一致。

    例如:

    min-slave-max-lag 10

读写分离与负载均衡

主从复制可以实现读写分离,以提高服务器的负载能力。在一些生产环境中,对数据库的读频率远远大于写,单个 redis 服务器无法处理这么庞大的请求,可以复制多个从节点分担读请求,主节点只复制写请求。所有的读操作则可以分散到多个从节点。可以通过在应用程序中配置连接到从节点的读连接来实现。这样可以减轻主节点的负担,提高整体系统的读取性能。例如,在应用程序的 Redis 连接池配置中,可以将读操作的连接指向多个从节点,实现读写分离。

在主从复制架构中,通常会使用代理或专门的负载均衡器,将请求分发到不同的 Redis 节点。这可以是基于软件的负载均衡器(如 HAProxy、Nginx)或者专门用于 Redis 的代理(如 Twemproxy、Redis Sentinel)。

持久化

为了提高性能,可以通过复制功能创建多个从节点,并在从节点启用持久化,在主节点禁用持久化。从节点崩溃重启后可以自动从主节点中将数据同步过来,所以无需担心数据丢失。

但是当主节点崩溃时,情况就比较复杂了,需要先将一个从节点作为主节点,然后再将崩溃的原主节点作为从节点来恢复数据。从当前的从节点中选择一个节点。这个从节点应该是与主节点数据最接近的,同时复制延迟最小的一个。使用 SLAVEOF NO ONE命令将其从从节点升级为主节点,然后启动之前崩溃的主数据库,使用 SLAVEOF将其设置为新的主节点的从节点即可同步数据。

当开启复制且关闭主节点的持久化时,一定不要使用一些管理工具令主节点崩溃后自动重启,这样启动后主节点数据不但不会恢复,还会因为复制而导致从节点拥有的数据全部清空。

无论哪种情况,手工维护从数据库或者主数据库的重启以及数据恢复都很麻烦,redis 提供了哨兵机制来自动化实现这一过程,避免手工维护可能出现的问题。这里就不介绍了,之后再总结。

无盘复制

传统的 Redis 复制过程中,主节点会将数据写入磁盘,并将数据传输给从节点进行复制。当主节点禁用持久化时,如果执行了复制初始化操作,redis 依然会生成 RDB 文件,所以下次启动后主节点会以该文件恢复数据。由于复制初始化操作时间不确定,因此可能会导致恢复的数据不完整。同时,每次和主节点同步时都需要执行一次快照,要磁盘中进行 IO 操作,可能会导致性能瓶颈。

redis 引入了无盘复制选项。在无盘复制中,主节点在复制过程中不需要将数据写入磁盘,而是直接将数据通过网络传输给从节点进行复制。这样可以减少磁盘 IO 的开销,提高复制的效率。

可以配置项 repl-diskless-sync yes 开启该功能。

增量复制

当从节点从主节点断开后,重连后从节点会发送 SYNC 命令来重新进行一次完整的复制操作,即使断开后的数据改变很小,也需要将完整的数据快照传输一份,这种方式显然不够理想。

redis 引入了主从断线重连情况下的增量复制。

增量复制机制基于以下三点

  1. 主节点的 Run ID: 每个 Redis 实例都有一个唯一的 Run ID,它是在实例启动时生成的。Run ID 在 Redis 集群中用于标识节点的身份。在增量复制中,主节点的 Run ID 会被从节点记录下来。这有助于从节点在断线重连后能够识别主节点是否发生了变化,如果发生变化,从节点可能需要进行全量同步。
  2. 主节点传递命令和队列: 在增量同步阶段,主节点会将每个写操作的命令传递给从节点。这些命令通常会存放在一个队列中,称为命令队列。主节点会记录当前队列中存放命令的偏移量。这个队列包含了主节点接收到的所有写命令,包括对数据结构的增、删、改等操作。
  3. 从节点记录命令的偏移量: 从节点接收到主节点传递的命令时,会记录下每个命令的偏移量。这个偏移量表示命令在主节点的队列中的位置。通过记录这些偏移量,从节点可以知道自己已经接收到了哪些命令,以及需要从主节点的队列中获取哪些新的命令。

当主从节点连接后,不再发送 SYNC 来同步,而是发送 PSYNC [主节点的 run id] [断开前最新的命令偏移量]这样格式的命令。主节点会执行下面的判断来确定是执行增量复制还是完整的复制操作:

  • 主节点首先判断 <run_id> 是否与当前主节点的 Run ID 相同。如果不同,可能意味着主节点已经发生了切换,需要进行全量同步。
  • 如果 <run_id> 相同,则主节点进一步判断 <offset> 是否在主节点的队列中。
  • 如果 <offset> 在队列中,主节点会执行增量复制,将从 <offset> 位置开始的命令发送给从节点,从而实现增量同步。
  • 如果 <offset> 不在队列中,或者其他判断条件不满足,可能会导致主节点拒绝增量同步请求,需要执行全量同步。

命令队列即 Redis 的复制积压缓冲区(replication backlog)是一个固定长度的循环队列,用于保存主节点的写操作记录,以便在从节点断线后,重新连接时提供增量复制所需的数据。

  1. repl-backlog-size

    • 默认情况下,复制积压缓冲区的大小是 1MB。
    • 通过设置 repl-backlog-size 配置选项,可以指定积压队列的大小,例如,设置为 repl-backlog-size 10mb 将队列大小增加到 10MB。
    • 较大的积压队列允许主从数据库断线的时间更长,因为从节点可以在断线后更长时间内获取到增量同步所需的数据。
  2. repl-backlog-ttl

    • 当没有从节点连接需要同步时,Redis 可以定期释放复制积压缓冲区中的数据。
    • repl-backlog-ttl 配置选项用于指定释放周期,单位是秒。例如,repl-backlog-ttl 3600 表示每小时释放一次积压缓冲区中的数据。
    • 这个配置项的存在是为了避免长时间没有从节点连接时,积压缓冲区不断增大,占用过多内存。
------本页内容已结束,喜欢请分享------

文章作者
能不能吃完饭再说
隐私政策
PrivacyPolicy
用户协议
UseGenerator
许可协议
NC-SA 4.0


© 版权声明
THE END
喜欢就支持一下吧
点赞24赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片