Redis(十):主从模式

前言

上一篇介绍了 Redis 应对并发问题的方案。这节开始介绍 Redis 的主从模式。

由于 Redis 是基于内存的,一旦发生崩溃就会导致数据的丢失,所以单个 Redis 实例并不能保证数据的可靠性。

为了应对高可靠性要求的场景,可以使用多个 Redis 实例作为某个节点的备份实例,从而实现了主从架构。

从节点可以作为主节点的备份节点,当主节点崩溃时,由于各个节点间的数据是相同的,还能够切换节点来保证整个集群的可靠性。使用了主从架构后,主从节点还可以分别处理写请求和读请求,从而实现了水平扩展,提高了整体的性能。

所以,使用主从架构有以下几个好处:

  • 提高可靠性:从节点可以视为主节点的备份节点,且当主节点故障时,可以替代主节点继续运行;
  • 提高性能:可以采用读写分离,减少主节点的负载;
  • 水平扩展:如果集群的读压力很大,可以添加从节点来分担。

主服务器可以进行读写操作,当发生写操作时(写入数据、key 的过期或驱逐等)自动将写操作同步给从服务器;而从服务器一般是只读,并接受主服务器同步过来的写操作命令,然后执行这条命令。

Redis(十):主从模式_第1张图片

# 服务器 B 执行命令后,就成了服务器 A 的从服务器
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>

主从复制

从节点是主节点的数据备份,同时也要处理读请求,所以要尽量保证主从数据一致。

主从复制,是指将主节点(master)上的数据,复制到其他从节点(slave)上。数据的复制是单向的,只能由主节点到从节点。

主从复制可以分为全量复制和增量复制。

全量复制

从节点第一次进行同步时,使用的是全量复制。

主从服务器间的第一次同步的过程可分为三个阶段:

  1. 建立链接、协商同步;
  2. 主服务器同步数据给从服务器;
  3. 主服务器发送新写操作命令给从服务器。

Redis(十):主从模式_第2张图片

第一阶段:建立链接、协商同步

执行了 replicaof 命令建立起连接后,从服务器就会给主服务器发送 psync 命令,表示要进行数据同步。

psync 命令包含两个参数,分别是主服务器的 runID复制进度 offset

  • runID,每个 Redis 服务器在启动时都会自动生产一个随机的 ID 来唯一标识自己。当从服务器和主服务器第一次同步时,因为不知道主服务器的 runID,所以将其设置为 “?”。
  • offset,表示复制的进度。第一次同步时,其值为 -1。

主服务器收到 psync 命令后,会用 FULLRESYNC 作为响应命令返回给对方。并且这个响应命令会带上两个参数:主服务器的 runID 和主服务器目前的复制进度 offset。从服务器收到响应后,会记录这两个值。

FULLRESYNC 响应命令的意图是采用全量复制的方式,也就是主服务器会把所有的数据都同步给从服务器。

第二阶段:主服务器同步数据给从服务器

主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。

这里有一点要注意,主服务器生成 RDB 这个过程是不会阻塞主线程的,因为 bgsave 命令是产生了一个子进程来做生成 RDB 文件的工作,是异步工作的,这样 Redis 依然可以正常处理命令。

但是,这期间的写操作命令并没有记录到刚刚生成的 RDB 文件中,这时主从服务器间的数据就不一致了。

那么为了保证主从服务器的数据一致性,主服务器在下面这三个时间间隙中将收到的写操作命令,写入到 replication buffer(复制缓冲区)里

  • 主服务器生成 RDB 文件期间;
  • 主服务器发送 RDB 文件给从服务器期间;
  • 「从服务器」加载 RDB 文件期间;

如果 replication buffer 满了,会导致连接断开,删除缓存,从节点重新连接,重新开始全量复制。

第三阶段:主服务器发送新写操作命令给从服务器

在主服务器生成的 RDB 文件发送完,从服务器收到 RDB 文件后,丢弃所有旧数据,将 RDB 数据载入到内存。完成 RDB 的载入后,会回复一个确认消息给主服务器。

接着,主服务器将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器 replication buffer 缓冲区里发来的命令,这时主从服务器的数据就一致了。

将 replication buffer 缓冲区里的操作发送给从服务器时会阻塞吗?

由于发送 replication buffer 缓冲区是由子进程 bgsave 异步操作的,所以并不会阻塞主服务器。如果在这个过程中又有新的数据写入,则会留到下一次复制。

为什么全量复制使用 RDB 而不使用 AOF?

  1. 因为 AOF 文件比 RDB 文件大,网络传输比较耗时;
  2. 从库在初始化数据时,RDB 文件比 AOF 文件执行更快。

增量复制

如果主从服务器在命令传播时出现了网络闪断恢复的情况,从服务器就会和主服务器重新进行一次全量复制,开销非常大。

所以从 Redis 2.8 开始,网络断开恢复之后,主从库会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。

流程:

  1. 从服务器在恢复网络后,会发送 psync 命令(参数 runid 和 offset)给主服务器,此时的 psync 命令里的 offset 参数不是 -1,而是自己的复制进度;
  2. 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据;
  3. 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。

Redis(十):主从模式_第3张图片

repl_backlog_buffer (复制积压缓冲区)是什么时候写入的?

在主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入到 repl_backlog_buffer 缓冲区里,因此这个缓冲区里会保存着最近传播的写命令。

网络断开后,当从服务器重新连上主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 slave_repl_offset 发送给主服务器,主服务器根据自己的 master_repl_offset 和 slave_repl_offset 之间的差距,然后来决定对从服务器执行哪种同步操作:

  • 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么主服务器将采用增量同步的方式;
  • 如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式。

由于 repl_backlog_buffer 是「环形」缓冲区,所以旧的写命令会被新的写命令覆盖。

除了断连外,在正常的主从复制过程中也会比对主从节点的偏移量,如果不匹配,也会触发增量复制。也就是说,正常情况下,除了第一次同步是全量复制,后续从节点都是通过增量复制来同步数据的。

当主服务器在 repl_backlog_buffer 中找到主从服务器差异(增量)的数据后,就会将增量的数据写入到 replication buffer,再发送给从服务器。

一个主节点只分配一个 repl backlog buffer(复制积压缓冲区);主节点会给每个新连接的从节点,分配一个 replication buffer(复制缓冲区);

主从级联模式

主从服务器在第一次数据同步的过程中,主服务器会做两件耗时的操作:生成 RDB 文件和传输 RDB 文件。

主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,就会带来两个问题:

  • 由于是通过 bgsave 命令来生成 RDB 文件的,那么主服务器就会忙于使用 fork() 创建子进程,如果主服务器的内存数据非大,在执行 fork() 函数时是会阻塞主线程的,从而使得 Redis 无法正常处理请求;
  • 传输 RDB 文件会占用主服务器的网络带宽,会对主服务器响应命令请求产生影响。

为了分担主服务器的压力,Redis 实现了从服务器可以有自己的从服务器,也就是说从服务器不仅可以接收主服务器的同步数据,自己也可以同时作为主服务器的形式将数据同步给从服务器。通过这种方式,主服务器生成 RDB 和传输 RDB 的压力可以分摊到充当经理角色的从服务器

Redis(十):主从模式_第4张图片

一致性

由于主从库复制是异步执行的,所以可能会导致主库和从库在短时间内数据不一致,如果此时有读请求到从库,就会造成主从一致性问题。

只能通过一些额外的机制来尽量避免主从数据不一致问题。

方法一:监测复制进度:

因为 Redis 的 INFO replication 命令可以查看主库接收写命令的进度信息(master_repl_offset)和从库复制写命令的进度信息(slave_repl_offset),所以,我们就可以开发一个监控程序,先用 INFO replication 命令查到主、从库的进度,然后,我们用 master_repl_offset 减去 slave_repl_offset,这样就能得到从库和主库间的复制进度差值了。

如果某个从库的进度差值大于我们预设的阈值,我们可以让客户端不再和这个从库连接进行数据读取,这样就可以减少读到不一致数据的情况。不过,为了避免出现客户端和所有从库都不能连接的情况,我们需要把复制进度差值的阈值设置得大一些。

这种办法只能减少数据不一致的情况,如果要严格地要求一致性,只能通过分布式锁等方法,在主从复制过程中阻塞客户端的读写请求。

最后

本文介绍了 Redis 的主从模式,为了提高主节点的性能,以及提高数据的可靠性以及整个集群的高可用性,可以给主节点设置多个从节点,并且采用读写分离的方式处理请求。主从节点间的数据要尽量保持一致,所以需要进行主从复制。由于主从复制是异步进行的,所以还需要适当地考虑数据一致性的问题。

下一节将介绍 Redis 的哨兵机制。

你可能感兴趣的:(Redis,redis,主从复制,主从架构)