乐趣区

关于redis:路上小胖问我Redis-主从复制原理是怎样的

00 前言

我负责我司的报表零碎,小胖是我小弟。随着业务量的减少,单实例顶不住,我就搭建了多个 Redis 实例,实现主从模式。

好学的小胖就问我啊,远哥,多实例之间的数据是怎么放弃同步的呀?你教教我好不好嘛~

我拿起手中 82 年的开水抿了一口,跟小胖说:你先看这篇文章,学会了操作,我再给你讲讲原理吧。

https://juejin.cn/post/684490…

老规矩,还是先上脑图:(PS:文末有我筹备的 大厂面试题

0.1 往期精彩

MySQL 查问语句是怎么执行的?

MySQL 索引

MySQL 日志

MySQL 事务与 MVCC

MySQL 的锁机制

Redis 根底

Redis 长久化

01 主从复制

Redis 的高牢靠次要由两点保障,一是 数据尽量少失落 ,二是 服务尽量少中断。长久化保障了第一点;而第二点则由 Redis 集群保障,Redis 的做法就是多实例保持数据同步。

1.1 读写拆散

Redis 提供了主从库模式,以保证数据正本的统一,主从库之间采纳的是读写拆散的形式。

  1. 读操作:主库、从库都能够接管;
  2. 写操作:首先到主库执行,而后,主库将写操作同步给从库。

为什么要读写拆散呢?

看看上图,如果所有库都能够写,那将会产生一个 key 在不同实例就有不同的值。比方上图对应的键 k1 在不同实例就有不同的 v1、v2、v3 值,这是相对不能承受的。

有人说,,能够加锁呀

然而这会波及到加锁、实例间协商是否实现批改等一堆操作,带来巨额的开销,Redis 以快著称,这也是不能承受的。

那咋办呀?读写拆散咯

主从库模式采纳读写拆散,所有数据批改只会在主库进行。主库有最新数据,会同步给从库,这样,主从库的数据就是统一的。

问题就在同步了,主从库之间是怎么同步的呢?一起来探讨下

1.2 全量复制

当咱们启动多个 Redis 实例的时候,它们相互之间就通过 replicaof(Redis 5.0 之前应用 slaveof)命令造成主库和从库的关系,之后会依照三个阶段实现数据的第一次同步。

比方当初有实例 1(127.0.0.1)和实例 2(127.0.0.2),在实例 2 执行 replicaof 后,实例 2 就变成实例 1 的从库啦。

replicaof  127.0.0.2  6379

建设关系之后,Redis 会进行第一次全量复制。过程如下:

第一步,主从库建设连贯、协商同步的过程,次要是为全量复制做筹备。

从库给主库发送 psync 命令,示意要进行数据同步,主库依据这个命令的参数来启动复制。psync 命令蕴含了 主库的 runID 和复制进度 offset 两个参数

  • runID,是每个 Redis 实例启动时都会主动生成的一个随机 ID,用来惟一标记这个实例。当从库和主库第一次复制时,因为不晓得主库的 runID,所以将 runID 设为“?”。
  • offset,此时设为 -1,示意第一次复制。主库收到 psync 命令后,会用 FULLRESYNC 响应命令带上两个参数:主库 runID 和主库目前的复制进度 offset,返回给从库

FULLRESYNC 响应示意第一次复制采纳的全量复制,也就是说,主库会把以后所有的数据都复制给从库。

第二步,主库执行 bgsave 命令,生成 RDB 文件,发送给从库。从库收到后,先清空本人的数据(防止主从不统一),在本地实现数据加载。

主库在将数据同步给从库的过程中,依然能够失常接管申请。然而,这些申请中的写操作并没有记录到刚刚生成的 RDB 文件中。

为了保障主从一致性,主库会在内存中用 replication buffer 记录 RDB 文件生成后收到的所有写操作。

第三步,主库会把 replication buffer 中的批改操作发给从库,从库再从新执行这些操作。如此,主从就实现了同步。

1.3 主从级联扩散压力

以上还有个问题:如果间接跟主库建设关系的从库十分多,那么 主库会 fork 很多线程去生成 RDB 和 发送 RDB 文件,这将会造成主库阻塞。那咋办?

主从级联的模式就来了:选一个资源较多的实例作为级联的从库,作为叶子节点的从库执行以下命令将所选从库作为主库,这样就造成了级联关系。

replicaof  所选从库的 IP 6379

如上图,主库就跟两个实例做主从关系,从库又跟另外的从库建设主从关系,相似于树结构。如此,主库便可缩小压力。

就上图来说,主库原来要跟 4 个从库建设主从关系;加了级联之后,只须要建设两个主从关系,剩下的由某一个从库去承当。这颇有点 父养子,子又养子 的意思。

这个过程也称为 基于长连贯的命令流传,能够防止频繁建设连贯的开销。

1.4 增量复制

细想一下,下面的主从复制还有问题:如果主从之间的网络断开了咋办?

其实 Redis 2.8 之前,主从断开了,做的是全量复制。但这种形式开销太大,已被淘汰。2.8 之后的 Redis 采纳的是 增量复制

增量复制依赖一个环形缓冲区 repl_backlog_buffer(只有有从库存在,就会有这个缓存区),主从断连后,主库会把断连期间的写操作命令,写入 repl_backlog_buffer 缓冲区。repl_backlog_buffer 是一个环形缓冲区,主库会记录本人写到的地位,从库则会记录本人曾经读到的地位

如上图所示,还没断连时,主从指向同一个地位;主库写,从库读 ,始终往前走。忽然,断连了, 从库没法写、主库持续读

复原连贯后,从库发送 psync {runID}{offset} 通知主库,本人上次读到哪。主库通过 offset 在 repl_backlog_buffer 中找到从库断开的地位,主从之间的偏移量就是增量复制须要同步的内容。比方上图的 put d e 和 put d f 两个操作。

把这部分增量数据复制到 repl_buffer 中,主库再发送给从库读入。

基于此 增量复制 的整个流程是这样的:

你可能也发现了?repl_backlog_buffer 是环形缓存区,如果从库断开工夫太久,就有可能导致从库还未读取的操作被主库新写的操作笼罩了,这会导致主从库间的数据不统一。这该咋办呀?

连贯复原后,主库依据从库上次读到的 offset 地位判断是否被笼罩?如果是,从库连上主库后也只能乖乖地进行一次全量同步

为了防止全量同步,能够通过参数 repl_backlog_size 设置 repl_backlog_buffer 的大小,把它弄大点。

计算公式是:缓冲空间大小 = 主库写入命令速度 操作大小 – 主从库间网络传输命令速度 操作大小

repl_backlog_size = 缓冲空间大小 * 2

这样一来,产生全量同步的概率就小了很多了。

02 总结

本文次要讲了 Redis 的主从模式为什么要读写拆散?Redis 全量复制的流程以及原理;从库很多时,如何升高主库复制的压力?如果主从断开了,Redis 是怎么进行数据同步的?心愿你看完能有所播种。

好啦,以上就是狗哥对于 Redis 主从复制的总结。感激各技术社区大佬们的付出,尤其是极客工夫,真的牛逼。如果说我看得更远,那是因为我站在你们的肩膀上。心愿这篇文章对你有帮忙,咱们下篇文章见~

2.1 伟人的肩膀

  • 《Redis 设计与实现》
  • time.geekbang.org/column/article/272852

03 大厂面试题 & 电子书

如果看到这里,喜爱这篇文章的话,请帮点个 难看

初次见面,也不晓得送你们啥。罗唆就送 几百本电子书 2021 最新面试材料 吧。微信搜寻 JavaFish 回复 电子书 送你 1000+ 本编程电子书;回复 面试 获取 50 套大厂面试题;回复 1024 送你一套残缺的 java 视频教程。

面试题都是有答案的,如下所示:有须要的就来拿吧,相对收费,无套路获取

退出移动版