自我系统学习Redis小记-03

06 | 数据同步:主从库如何实现数据一致?

1、Redis 具有高可靠性,具体是指?

1)、数据少丢失。 AOF和RDB

2)、服务尽量少中断。 增加副本冗余量

2、多个副本实例保存同一份数据,他们之间怎么保证数据一致性?数据读写操作可以发送给所有实例吗?

Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。

读操作:主库、从库都可以接收;

写操作:首先到主库执行,然后,主库将写操作同步给从库。

Redis主从库和读写分离

为什么要采用读写分离的方式?

假如不使用读写分离,如果一个数据前后修改三次,分别在不同的实例上做的修改,那么这个数据在三个副本上就不一致了,读的时候就可能读取到之前的数据,要保证数据在三个副本上一致,就涉及到加锁、各实例间协商完成修改等的巨额开销。

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

留下问题!!!!重点

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

3、主从库间如何进行第一次同步?

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

例如,1、2两台实例,可以在2实例上执行以下命令,ip为1的ip

replicaof 172.16.19.3 6379

第一次主从同步的三个阶段:

主从库第一次同步的流程

第一阶段:是主从库间建立连接、协商同步的过程,主要是为全量复制做准备。

在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了

具体来说,从库给主库发送 psync 命令,表示要进行数据同步,主库根据这个命令的参数来启动复制。

psync 命令包含了主库的 runID 复制进度 offset 两个参数。

1)、runID,是每个 Redis 实例启动时都会自动生成的一个随机 ID,用来唯一标记这个实例。当从库和主库第一次复制时,因为不知道主库的 runID,所以将 runID 设为“?”。

2)、offset,此时设为 -1,表示第一次复制。

主库收到 psync 命令后会用 FULLRESYNC响应命令,并且带上两个参数,返回给从库,从库会记录。

注意,FULLRESYNC 响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库

第二阶段:主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载

这个过程依赖于内存快照生成的RDB文件。

具体来说:主库执行bsave命令生成RDB文件,发送给从库,从库先清空自己当前的数据库,再执行RDB文件。(防止之前保存了其他数据)

主库同步从库时,不会中断,仍可以正常接受请求,其中写操作并没有被记录在RDB文件中,而是会在内存中用专门的replication buffer 记录RDB文件生成后收到的所有写操作。

第三阶段:主库会把第二阶段执行过程中新收到的写命令,再发送给从库,从库执行,主从库实现同步

4、主从级联模式分担全量复制时的主库压力

一次全量复制中,主库的压力在于生成RDB文件、传输RDB文件。

如果从库数量多,都要从主库全量同步,会导致主库忙于fofork主线程生成RDB文件,fork会阻塞主线程处理正常请求,导致主库相应应用程序较慢。此外,传输RDB文件会占用主库网络带宽,同样会给主库带来压力。

解决方法:可以分担主库压力?

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

级联的“主-从-从”模式

简单来说,在部署主从集群时,可以手动的选一个从库(配置比较高的从库),用于级联其他从库,然后再选择一些其他从库(例如三分之一从库),执行如下命令,让它们和刚才选择的从库建立起主从关系。

replicaof 所选从库IP 6379

这样一来,从库就知道在进行同步时,不需要跟主库进行交互,只要和级联的从库同步写操作就可以了这样可以减轻主库压力

级联的“主-从-从”模式

一旦从库完成了全量复制,,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,可以避免频繁建立连接的开销。

风险:网络断链或阻塞

如果网络断连,主从库之间就无法进行命令传播了,从库的数据自然就没发和主库保持一致了,客户端就可能读到旧数据

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

网络断开后,主从库会采用增量复制的方式进行复制,即把网络断开这段时间主库收到的命令,同步给从库。

增量复制时,主从库之间具体是怎么保持同步的呢?

repl_backlog_buffer 缓冲区

当主从库断连后,主库会把断连期间收到的写操作命令,写入 replication buffer(这里应该是无论断不断连接,都要先写入这个缓冲区),同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。

repl_backlog_buffer:是主库记录写命令的,所有从库共享。从命名可以看出,用于备份的缓存区,因此该缓存区存在的前提是需要有从库,没有从库该缓存区就会被回收。该缓存区是循环写的,因此发生数据追尾是必然的,也就是说该缓存区内的数据是最新的一部分数据而非全部数据。这部分数据唯一的用途是为从库弥补差异化数据的,如果从库相比主库数据差异太大,就不能从该缓存区取数据了,只能进行全量同步。

replication_buffer: 该缓存区本质上是一个 client_buffer,从命名也能看出,是每个客户端都对应一个。一般情况下数据是先写入 client_buffer 然后再发送到客户端的 socket 。在主从同步时,也就是先把指令写入 replication_buffer,然后在发送到从库。 

repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置。

这里会有两个偏移量,master_repl_offset 主库新写偏移量slave_repl_offset 从库已复制偏移量

正常情况下,这两个偏移量基本相等,

Redis repl_backlog_buffer的使用

主从库的连接恢复之后,从库首先会给主库发送 psync 命令,并把自己当前的slave_repl_offset 发给主库,主库会判断自己的 master_repl_offset 和 slave_repl_offset之间的差距。

一般情况master_repl_offset 会大于 slave_repl_offset ,主库只把这之间的命令同步给从库即可。

Redis恢复连接后增量复制流程

这里注意,repl_backlog_buffer 是一个环形缓冲区,在写满后会覆盖之前的命令,如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这会导致主从库间的数据不一致。

一般会调整 repl_backlog_size 这个参数,这个参数和缓存空间大小有关系。

repl_backlog_size = 缓冲空间大小 * 2

(缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小)

如果并发比较多,可以考虑设置成4倍

如果它配置得过小,在增量复制阶段,可能会导致从库的复制进度赶不上主库,进而导致从库重新进行全量复制。所以,通过调大这个参数,可以减少从库在网络断连时全量复制的风险。

6、小结

Redis 的主从库同步的基本原理,总结来说,有三种模式:全量复制、基于长连接的命令传播,以及增量复制。

1、全量复制耗时,对于从库来说,第一次无法避免,小建议:一个 Redis 实例的数据库不要太大,一个实例大小在几 GB 级别

比较合适,这样可以减少 RDB 文件生成、传输和重新加载的开销。避免多个从库复制给主库带来压力,可以采用主-从-从方式

来缓解。

2、基于长链接的命令传播是正常情况下主从常规同步阶段,主从库之间通过命令传播实现同步。

3、如果出现网络断开,增量复制上场,这里注意repl_backlog_size 这个参数设置大一些。

7、每课一问

主从库间的数据复制同步使用的是RDB 文件,前面我们学习过,AOF 记录的操作命令更全,相比于 RDB 丢失的数据更少。

那么,为什么主从库间的复制不使用 AOF 呢?

简单来说:AOF比RDB大,RDB加载起来比aof快。


07 | 哨兵机制:主库挂了,如何不间断服务?

1、主从库一旦主库挂了,无法处理写操作、我要发完成后续数据同步

主库故障后从库无法服务写操作

涉及三个问题:

1)、主库真的挂了么?

2)、该选择哪个从库为主库?

3)、怎么把❤️主库信息同步给从库和客户端?

哨兵机制完美解决了上面三个问题,在 Redis 主从集群中,哨兵机制是实现主从库自动切换的关键机制

2、哨兵机制的基本流程

哨兵是一个运行在特殊模式下的Redis进程,主从库运行的同时,它也在运行。

哨兵的主要任务是:监控、选主、和通知。

1)、监控:监控是哨兵进程在运行时,周期性的给所有的主从库发送 ping 命令,检测他们是否仍然在线运行。

如果从库没有在规定时间内响应哨兵的ping,哨兵就会把从库标记为“下限状态”,

同样,主库如果没有在规定时间内响应哨兵的ping,哨兵就会判定主库下线,然后开始自动切换主库的流程。

2)、选主:主库挂了,哨兵需要从很多个从库中按照一定的规则选择一个从库实例,把它作为新的主库

3)、通知:在执行通知任务时,哨兵会把新主库的连接信息发给其他从库,让它们执行 replicaof 命令,和新主库建立连接,

并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。

哨兵机制的三项任务与目标

这三步中,监控和选主中哨兵涉及到两个决策:

    在监控任务中,哨兵需要判断主库是否处于下线状态;

    在选主任务中,哨兵也要决定选择哪个从库实例作为主库。

3、哨兵对主库的下线判断有“主观下线”和“客观下线”两种,为什么会存在两种判断?区别和联系?

主观下线:哨兵进程会使用 PING 命令检测它自己和主、从库的网络连接情况,用来判断实例的状态。如果哨兵发现主库或从库对 PING 命令的响应超时了,那么,哨兵就会先把它标记为“主观下线”。

从库的话--->直接标记“主观下线”

主库的话,还不能直接标记主管下线,很有可能是因为网络波动,哨兵误判了,主库其实并没有挂掉

可是,一旦启动了主从切换,后续的选主和通知操作都会带来额外的计算和通信开销。

误判一般会发生在集群网络压力较大、网络拥塞,或者是主库本身压力较大的情况下。

怎么减少误判,哨兵机制也是类似的,它通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。

多个哨兵网络同时不稳定的概率较小,由他们一起做决策,会减小误判的概率。

只有多个哨兵标记主库“主管下线”,主库才会被“客观下线”。

客观下线的判断

哨兵集群可以减少误判的概率,也能避免误判带来的无谓的主从库切换。这个判断的个数可以由管理员自行设置。

4、如何选定新主库?

哨兵选择过程:筛选+打分

先按照一定条件筛选,再按照一定规则逐一进行打分,分数最高的被选为新主库。

新主库的选择过程

1)、筛选:在选主时,除了要检查从库的当前在线状态,还要判断它之前的网络连接状态

2)、打分:从库优先级、从库复制进度以及从库 ID 号进行三轮打分

只要在某一轮中,有从库得分最高,那么它就是主库了,选主过程到此结束。如果没有出现得分最高的从库,那么就继续进行下一轮。

第一轮:优先级最高的从库得分最高;指的是有些从库内存不一样,可以手动给从库设置优先级,如果有一个从库优先级最高,那么它成为新主库,选主结束,否则进入第二轮

第二轮:和旧主库同步程度最接近的从库得分高

主从库命令传播新写操作的日志记录在环形缓冲区中,主库写操作记录到master_repl_offset,各个从库复制到slave_repl_offset,哪个比较接近哪个复制的多成为主库

基于复制进度的新主库选主原则

如图,从库2成为新主库。如果有多个相同,则进入第三轮。

第三轮:ID号小的从库得分高;

在优先级和复制进度都相同的情况下,ID 号最小的从库得分最高,会被选为新主库。

ID就相当于每个从库的编号

5、小结

1)、哨兵机制,它是实现 Redis 不间断服务的重要保证

主从集群的数据同步,是数据可靠的基础保证;而在主库发生故障时,自动的主从切换是服务不间断的关键支撑。

哨兵三大功能:监控、选主、通知

为减少误判,哨兵也采取集群方式部署

少数服从多数,界定主库的主观下线、客观下线

2)、但是新的问题?----解决(哨兵集群)

哨兵集群中有实例挂了,怎么办,会影响主库状态判断和选主吗?

哨兵集群多数实例达成共识,判断出主库“客观下线”后,由哪个实例来执行主从切换

呢?

6、每课一问

通过哨兵机制,可以实现主从库的自动切换,这是实现服务不间断的关键支撑,同时,我也提到了主从库切换是需要一定时间的。所以,请你考虑下,在这个切换过程中,客户端能否正常地进行请求操作呢?如果想要应用程序不感知服务的中断,还需要哨兵或需要客户端再做些什么吗?

如果客户端使用了读写分离,那么读请求可以在从库上正常执行,不会受到影响。但是由于此时主库已经挂了,而且哨兵还没有选出新的主库,所以在这期间写请求会失败,失败持续的时间 = 哨兵切换主从的时间 + 客户端感知到新主库 的时间。

你可能感兴趣的:(自我系统学习Redis小记-03)