Redis的主从复制是如何做的?复制过程中也会产生各种问题?

如果master复制缓冲区找不到offset之后的数据,说明断开的时间太久,复制缓冲区的内容已经被新的内容覆盖了,此时master只能触发全量数据同步。

命令传播


slave经过全量同步或部分同步后,之后master实时产生的写入,是如何实时同步的?

很简单,master每次执行完新的写入命令后,也会把这个命令实时地传播给slave,slave执行与master相同的操作,就可以实时与master保持一致。

需要注意的是,master传播给slave的命令是异步执行的,也就是说在master上写入后,马上在slave上查询是有可能查不到的,因为异步执行存在一定的延迟。

slave与master建立连接后,slave就属于master的一个client,master会为每个client分配一个client output buffer,master和每个client通信都会先把数据写入到这个内存buffer中,再通过网络发送给这个client。

但是,由于这个buffer是占用Redis实例内存的,所以不能无限大。所以Redis提供了控制buffer大小的参数限制:

普通client buffer限制

client-output-buffer-limit normal 0 0 0

slave client buffer限制

client-output-buffer-limit slave 256mb 64mb 60

pubsub client buffer限制

client-output-buffer-limit pubsub 32mb 8mb 60

这个参数的格式为:client-output-buffer-limit $type $hard_limit $soft_limit $soft_seconds,其含义为:如果client的buffer大小达到了hard_limit或在达到了soft_limit并持续了soft_seconds时间,那么Redis会强制断开与client的连接。

对于slave的client,默认的限制是,如果buffer达到了256MB,或者达到64MB并持续了1分钟,那么master就会强制断开slave的连接。

这个配置的大小在某些场景下,也会影响到主从的数据同步,我们下面会具体介绍到。

心跳机制


在命令传播阶段,为了保证master-slave数据同步的稳定进行,Redis还设计了一些机制维护这个复制链路,这种机制主要通过心跳来完成,主要包括两方面:

  • master定时向slave发送ping,检查slave是否正常

  • slave定时向master发送replconf ack $offset,告知master自己复制的位置

在master这一侧,master向slave发送ping的频率由repl-ping-slave-period参数控制,默认10秒,它的主要作用是让slave节点进行超时判断,如果slave在规定时间内没有收到master的心跳,slave会自动释放与master的连接,这个时间由repl-timeout决定,默认60秒。

同样,在slave这边,它也会定时向master发送replconf ack $offset命令,频率为每1秒一次,其中offset是slave当前复制到的数据偏移量,这么做的主要作用如下:

  • 让master检测slave的状态:如果master超过repl-timeout时间未收到slave的replconf ack $offset命令,则master主动断开与slave的连接

  • master检测slave丢失的命令:master根据slave发送的offset并与自己对比,如果发现slave发生了数据丢失,master会重新发送丢失的数据,前提是master的复制缓冲区中还保留这些数据,否则会触发全量同步

  • 数据安全性保障:Redis提供了min-slaves-to-writemin-slaves-max-lag参数,用于保障master在不安全的情况下禁止写入,min-slaves-to-write表示至少存在N个slave节点,min-slaves-max-lag表示slave延迟必须小于这个时间,那么master才会接收写命令,否则master认为slave节点太少或延迟过大,这种情况下是数据不安全的,实现这个机制就依赖slave定时发送replconf ack $offset让master知晓slave的情况,一般情况下,我们不会开启这个配置,了解一下就好

可见,master和slave节点通过心跳机制共同维护它们之间数据同步的稳定性,并在同步过程中发生问题时可以及时自动恢复。

我们可以可以在master上执行info命令查看当前所有slave的同步情况:

role:master # redis的角色

connected_slaves:1 # slave节点数

slave0:ip=127.0.0.1,port=6480,state=online,offset=22475,lag=0 # slave信息、slave复制到的偏移位置、距离上一次slave发送心跳的时间间隔(秒)

master_repl_offset:22475 # master当前的偏移量

repl_backlog_ 需要zi料+ 绿色徽【vip1024b】

active:1 # master有可用的复制缓冲区

repl_backlog_size:1048576 # master复制缓冲区大小

通过这些信息,我们能看到slave与master的数据同步情况,例如延迟了多大的数据,slave多久没有发送心跳给master,以及master的复制缓冲区大小。

复制过程中的问题

===========================================================================

在整个数据复制的过程中,故障是时有发生的,例如网络延迟过大、网络故障、机器故障等。

所以在复制过程中,有一些情况需要我们格外注意,必要时需要针对性进行参数配置的调整,否则同步过程中会发生很多意外问题。

主要问题分为以下几个方面,下面依次来介绍。

主从断开复制后,重新复制触发了全量同步?


上面我们有提到,主从建立同步时,优先检测是否可以尝试只同步部分数据,这种情况就是针对于之前已经建立好了复制链路,只是因为故障导致临时断开,故障恢复后重新建立同步时,为了避免全量同步的资源消耗,Redis会优先尝试部分数据同步,如果条件不符合,才会触发全量同步。

这个判断依据就是在master上维护的复制缓冲区大小,如果这个缓冲区配置的过小,很有可能在主从断开复制的这段时间内,master产生的写入导致复制缓冲区的数据被覆盖,重新建立同步时的slave需要同步的offset位置在master的缓冲区中找不到,那么此时就会触发全量同步。

如何避免这种情况?解决方案就是适当调大复制缓冲区repl-backlog-size的大小,这个缓冲区的大小默认为1MB,如果实例写入量比较大,可以针对性调大此配置。

但这个配置不能调的无限大,因为它会额外占用内存空间。如果主从断开复制的时间过长,那么触发全量复制在所难免的,我们需要保证主从节点的网络质量,避免频繁断开复制的情况发生。

master写入量很大,主从断开复制?


主从经过全量同步和部分同步后,之后master产生了写入命令,会实时传播给slave节点,如果在这个过程中发生了复制断开,那么一定是在这个过程中产生了问题。我们来分析这个过程是如何处理命令传播的。

上面我们也提到了,主从建立同步链路后,由于slave也是master的一个client,master会对每个client维护一个client output buffer,master产生写命令执行完成后,会把这个命令写入到这个buffer中,然后等待Redis的网络循环事件把buffer中数据通过Socket发送给slave,发送成功后,master释放buffer中的内存。

如果master在写入量非常大的情况下,可能存在以下情况会导致master的client output buffer内存持续增长:

  • 主从节点机器存在一定网络延迟(例如机器网卡负载比较高),master无法及时的把数据发送给slave

  • slave由于一些原因无法及时处理master发来的命令(例如开启了AOF并实时刷盘,磁盘IO负载高)

当遇到上面情况时,master的client output buffer持续增长,直到触发默认配置的阈值限制client-output-buffer-limit slave 256mb 64mb 60,那么master则会把这个slave连接强制断开,这就会导致复制中断。

之后slave重新发送复制请求,但是以上原因可能依旧存在,经过一段时间后又产生上述问题,主从连接再次被断开,周而复始,主从频繁断开链接,无法正常复制数据

解决方案是,适当调大client-output-buffer-limit的阈值,并且解决slave写入慢的情况,保证master发给slave的数据可以很快得处理完成,这样才能避免频繁断开复制的问题。

添加slave节点,master发生阻塞?


当主从建立同步进行全量同步数据时,master会fork出一个子进程,扫描全量数据写入到RDB文件中。

这个fork操作,并不是没有代价的。fork在创建子进程时,需要父进程拷贝一份内存页表给子进程,如果master占用的内存过大,那么fork时需要拷贝的内存页表也会比较耗时,在完成fork之前,Redis整个进程都会阻塞住,无法处理任何的请求,所以业务会发现Redis突然变慢了,甚至发生超时的情况。

我们可以执行info可以看到latest_fork_usec参数,单位微妙。这就是最后一次fork的耗时时间,我们可以根据这个时间来评估fork时间是否符合预期。

对于这种情况,可以优化方案如下:

  • 一定保证机器拥有足够的CPU资源和内存资源

  • 单个Redis实例内存不要太大,大实例拆分成小实例

通过以上方式避免fork引发的父进程长时间阻塞问题。

主从全量同步数据时很慢?


之前我们已经了解到,主从全量复制会经过3个阶段:

最后

金三银四到了,送上一个小福利!

image.png

image.png

专题+大厂.jpg

之前我们已经了解到,主从全量复制会经过3个阶段:

最后

金三银四到了,送上一个小福利!

[外链图片转存中…(img-ejRmf5rx-1710365587901)]

[外链图片转存中…(img-W4zCx2ho-1710365587902)]

[外链图片转存中…(img-c3avBsVL-1710365587902)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值