redis核心技术与实战系列3:基础篇C

这节课,我就和你聊聊主从库同步的原理,以及应对网络断连风险的方案。主从库同步是如何完成的呢?
主库数据是一次性传给从库,还是分批同步?
要是主从库间的网络断连了,数据还能保持一致吗?

一、主从数据库如何实现数据一致

如果 Redis 发生了宕机, AOF 和 RD可以分别通过回放日志重新读入 RDB 文件的方式恢复数据,从而保证尽量少丢失数据,提升可靠性。

那如何面对服务不可用的问题呢?比如说,我们在实际使用时只运行了一个 Redis 实例,如果这个实例宕机了,它在恢复期间,是无法服务新来的数据存取请求的。

Redis 具有高可靠性的两层含义:
  • 一是数据尽量少丢失,
  • 二是服务尽量少中断。

AOF 和 RDB 保证了前者,而对于后者,Redis 的做法就是增加副本冗余量,将一份数据同时保存在多个实例上。即使有一个实例出现了故障,其他实例也可以对外提供服务,不会影响业务使用。

副本之间的数据如何保持一致呢?数据读写操作可以发给所有的实例吗?
主从库模式

用于保证数据副本的一致,主从库之间采用的是读写分离的方式。

  • 读操作:主库、从库都可以接收;
  • 写操作:首先到主库执行,然后,主库将写操作同步给从库。

为什么要采用读写分离的方式呢?
假设主库或者从库都能接收客户端的写操作:如果客户端对同一个前后修改了三次,每一次的修改请求都发送到不同的实例上,在不同的实例上执行,那么,这个数据在这三个实例上的副本就不一致了。导致读取的数据可能会是旧数据。

如果强制要保持这个数据在三个实例上一致,会涉及到加锁、实例间协商是否完成修改等一系列操作,会带来巨额的开销。

主从库模式采用了读写分离模式,所有数据的修改只会在主库上进行,不用协调三个实例。主库有了最新的数据后同步给从库,保证了主从库的数据一致性。

主从库同步是如何完成的呢?

主库数据是一次性传给从库,还是分批同步?
要是主从库间的网络断连了,数据还能保持一致吗?
主从库间如何进行第一次同步?

当我们启动多个 Redis 实例的时候,它们相互之间就可以通过 replicaof命令形成主库和从库的关系,之后会按照三个阶段完成数据的第一次同步。

例如,现在有实例 1(ip:172.16.19.3)和实例 2(ip:172.16.19.5),我们在实例 2 上执行以下这个命令后,实例 2 就变成了实例 1 的从库,并从实例 1 上复制数据:
replicaof 172.16.19.3 6379

主从库间数据第一次同步的三个阶段
  1. 第一阶段是主从库间建立连接、协商同步的过程。
    从库向主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。psync 命令包含了主库的 runID 和复制进度 offset 两个参数。
    runID是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。offset,此时设为 -1,表示第一次复制。
    主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库。从库收到响应后,会记录下这两个参数。
  2. 主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的 RDB 文件。
    主库执行 bgsave 命令生成 RDB 文件,然后将文件发给从库,从库接收到 RDB 文件后,会先清空当前数据库,然后加载 RDB 文件。
    这些请求中的写操作并没有记录到刚刚生成的 RDB 文件中。为了保证主从库的数据一致性,主库会在内存中用专门的 replication buffer,记录 RDB 文件生成后收到的所有写操作。
  3. 主库会把第二阶段执行过程中新收到的写命令,再发送给从库.
    当主库完成 RDB 文件发送后,就会把此时 replication buffer 中的修改操作发给从库,从库再重新执行这些操作。这样一来,主从库就实现同步了。
主从级联模式分担全量复制时的主库压力

分析主从库间第一次数据同步的过程可知,一次全量复制对主库来说存在两个耗时操作:生成 RDB 文件和传输 RDB 文件
如果从库数量很多,而且都要和主库进行全量复制的话,就会导致主库忙于 fork 子进程生成 RDB 文件,进行数据全量同步。fork 这个操作会阻塞主线程处理正常请求,从而导致主库响应应用程序的请求速度变慢。此外,传输 RDB 文件也会占用主库的网络带宽,同样会给主库的资源使用带来压力。那么,有没有好的解决方法可以分担主库压力呢?
其实是有的,这就是“主 - 从 - 从”模式。

主 - 从 - 从”模式

通过"主 - 从 - 从"模式将主库生成 RDB 和传输 RDB 的压力,以级联的方式分散到从库上。

在部署主从集群的时候,可以手动选择一个从库(比如选择内存资源配置较高的从库),用于级联其他的从库。然后,我们可以再选择一些从库和刚才所选的从库,建立起主从关系。
replicaof 所选从库的IP 6379

主从库完成了全量复制之后它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。
这个过程中存在着风险点,最常见的就是网络断连或阻塞。如果网络断连,主从库之间就无法进行命令传播了,从库的数据自然也就没办法和主库保持一致了,客户端就可能从从库读到旧数据。
接下来,我们就来聊聊网络断连后的解决办法。

主从库间网络断了怎么办?

在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。
从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。听名字大概就可以猜到它和全量复制的不同:全量复制是同步所有数据,而增量复制只会把主从库网络断连期间主库收到的命令,同步给从库。

增量复制

增量复制时主从库之间具体是怎么保持同步的呢?奥妙在于 repl_backlog_buffer 这个缓冲区。我们先来看下它是如何用于增量命令的同步的。
当主从库断连后,主库会把断连期间收到的写操作命令,写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。
repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置。
刚开始的时候,主库和从库的写读位置在一起,这算是它们的起始位置。随着主库不断接收新的写操作,它在缓冲区中的写位置会逐步偏离起始位置,我们通常用偏移量来衡量这个偏移距离的大小,对主库来说,对应的偏移量就是 master_repl_offset。主库接收的新写操作越多,这个值就会越大。
同样,从库在复制完写操作命令后,它在缓冲区中的读位置也开始逐步偏移刚才的起始位置,此时,从库已复制的偏移量 slave_repl_offset 也在不断增加。正常情况下,这两个偏移量基本相等。

主从库的连接恢复之后,从库首先会给主库发送 psync 命令,并把自己当前的 slave_repl_offset 发给主库,主库会判断自己的 master_repl_offset 和 slave_repl_offset 之间的差距。
在网络断连阶段,主库可能会收到新的写操作命令,所以,一般来说,master_repl_offset 会大于 slave_repl_offset。此时,主库只用把 master_repl_offset 和 slave_repl_offset 之间的命令操作同

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值