ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Redis主从同步全量与增量链路深度解析:从事故到调优

Redis主从同步全量与增量链路深度解析:从事故到调优 如果让我用一个词概括Redis主从同步我会选“链路”而不是“复制”。很多人在接触Redis的第一天就想当然地以为主从同步嘛无非就是主库把每次写命令转给从库执行一遍。这个理解对了一半但它完全解释不了我在生产环境遇到过的一个真实问题——某天从库因为磁盘故障重启后主库的IO在一分钟之内被打满整个业务出现了一连串读写超时。原因并不复杂从库重连时没能走增量同步被迫触发了一次全量同步主库为了生成RDB快照而fork子进程几十GB的数据在短时间内开始向从库传输把磁盘和带宽都打穿了。这篇文章我想把Redis主从同步的全量同步和增量同步一条链路拆透它们各自的触发条件、内部实现流程、由哪些核心机制在支撑以及在真实环境里应该怎么排查和调优。无论你是刚接触Redis复制的小白还是在生产环境里把主从用了一年半载的运维或开发这篇文章都值得看完——因为真正出事故的时候往往就藏在这些看起来“没啥好研究”的细节里。1. 先还原一个事故现场从库重启为什么会拖垮主库1.1 一次凌晨重启引发的主库毛刺有一段时间我维护的某套环境里从库节点的磁盘报警运维侧做了重启处理。按我的预期从库起来之后只需要连回主库把断线期间的新命令补上就行整个过程应该在几十秒内安静地结束。但实际观测到的现象是从库重启后的前十分钟主库的fork耗时从平时的几毫秒跳到了接近一秒磁盘写吞吐翻了三倍网卡出方向流量跑满业务侧随后报出大量延迟毛刺。真正的原因在从库重连之后那一刻就已经埋下了。从库发起的同步请求并没有得到“继续增量”的答复而是收到了主库的FULLRESYNC响应这代表它必须接收一份全量RDB快照把自己整个数据库重置一遍。主库为了准备这份快照需要执行BGSAVE而BGSAVE的第一步就是fork子进程。fork那一刻主库要复制一份自己完整的页表进程内存越大fork阻塞越明显。我遇到的那个实例内存接近40GBfork期间单线程的Redis主进程几乎停顿了数百毫秒这就是访问毛刺的来源。这个事故让我意识到一个关键点主从同步在绝大多数时间里是安静的增量流但一旦条件不满足它会瞬间退化成一次代价高昂的全量同步。而这个退化过程对不熟悉内部机制的人来说几乎是没法提前预判的。1.2 全量与增量不是平行路径而是兜底关系很多人会把全量同步和增量同步理解成两条平行的同步路径好像Redis可以随时在两者之间切换。实际上它们的关系完全不对等增量同步才是常态全量同步是兜底方案。我们可以用一句话概括各自的行为全量同步当从库没有任何主库的复制历史或者它的复制历史已经无法被主库找回时主库把整个数据库的RORDB快照直接给从库从库清空自己然后加载整份快照。这个过程的代价和网络传输的数据量成正比。增量同步从库已经持有主库的某段复制历史且主库本地保留着这段历史之后的命令流于是主库只需要把从库断线或落后期间的新命令发给从库从库顺序执行即可追平。我把两个路径的核心差异做成了一个对照表方便你记对比项全量同步增量同步触发条件从库无复制历史或历史已不可追溯从库持有可匹配的replid且offset仍在主库backlog内数据载体整份RDB快照一段连续的命令流传输量与全库体积相当可能几十GB与落后的命令量相当通常几MB以内对主库影响fork子进程、磁盘IO、带宽压力大几乎无额外资源消耗对从库影响清空本地数据、加载RDB期间不可服务无阻塞风险这张表之后很多人常问的一句话是那从库第一次加入主从的时候肯定要走全量之后一直增量理论上是这样。但生产环境远比理想情况复杂网络抖一下、backlog配小了、主从切换了、从库重启了……任何一个条件变化都可能让本应走增量的重连突然变成全量。后面的章节我会把这些触发条件全部展开。1.3 先建立一条完整的同步生命周期在进入细节之前我建议你先在脑子里建立这样一条生命周期线从库发起PSYNC请求携带自己当前的复制IDreplid和复制偏移量offset。主库根据这两项信息做一次判断这个从库是第一次来还是已经同步过我本地还有没有它落后的那段命令流如果可以增量主库返回CONTINUE然后开始持续发命令如果不行主库返回FULLRESYNC然后走全量把RDB快照发给从库。全量同步完成后主库会把快照之后产生的新命令补发给从库从库就此进入增量跟随状态。此后从库会定时向主库回报自己的offsetACK主库则定期PING从库用于感知它是否还活着、是否落后太多。这条线里第1、2步是入口第4步是全量流程的执行过程第5步是增量的运营机制。明白了这些骨架后面的每一个细节都有地方安放。2. 全量同步完整流程一份RDB快照的旅行2.1 第一次握手PSYNC ? -1之后主库做了什么在Redis 2.8之前从库只会发送SYNC命令主库看一次就无条件做一次全量复制压根没有增量这个概念。2.8之后引入的PSYNC改变了这个局面从库可以告知主库“我在哪条复制流上、进展到哪里了”让主库有机会决定是否走增量。当一台全新的从库第一次连接主库时它不知道任何replid所以会发送这样的命令PSYNC ? -1这里的-1表示“我的复制偏移量是未知的”。主库收到之后能够判断出这台从库没有任何复制历史于是直接回复FULLRESYNC master_replid master_repl_offset这段回复里有两个关键字段一个是主库本次启动后生成的master_replid一个是从库即将从哪个offset开始接收数据。从库收到后会把这个replid和offset记下来成为它自己复制身份的起点。之后它再向主库发PSYNC时就会带上这两个值。如果你在从库上执行redis-cli info replication会看到类似这样的输出role:slave master_host:10.0.0.11 master_port:6379 master_link_status:up master_replid:6f3c4f9a1e2d... slave_repl_offset:883040186这里的master_replid就是握手阶段从主库那里继承下来的身份标识slave_repl_offset则是从库当前的复制进度。它们是后续增量同步判断中的两个核心变量。2.2 为什么全量传输的载体必须选RDB快照回答一个很容易被忽略的问题为什么全量同步非要传RDB而不是把AOF里的命令逐条发给从库让它重放一遍先说结论因为RDB是二进制快照加载效率远高于命令重放。一份包含几百万个键的RDB从库加载可能只需要几十秒但如果换成一堆写命令哪怕只重放其中一部分都可能要跑几分钟甚至更久。更关键的是RDB快照能保证数据状态的一致性主库在某个时刻把整个数据集的镜像给从库从库加载完成后拿到的是一个与主库完全同一时刻的数据视图。当然RDB也不是没有代价。主库要得到这份RDB必须先通过BGSAVE把内存中的全量数据序列化到磁盘上。这个序列化的过程涉及fork子进程、写入临时文件、然后才能传输。所以你可以发现全量同步不是“一边读内存一边发数据”而是“先落盘、再读取发送”。这里的落盘动作也就是我在前面事故里提到的那个引发IO飙升的真正原因。如果使用无盘复制repl-diskless-sync主库可以直接在内存中生成RDB流式发送给从库但这会在后续单独讲。还有一层理解从库拿到RDB后是要清空现有数据再加载的。这意味着全量同步天然来不得半点“增量”的语义——它是把从库整个生命周期重置一次。正因为破坏性这么强生产环境才应该想尽办法避免频繁触发全量同步。2.3 BGSAVE期间的新写入repl_backlog默默兜底全量同步的过程并不是主库先把RDB发给从库这期间所有写命令暂停。Redis没有这么笨。主库在收到PSYNC请求后会立刻触发BGSAVE但与此同时它对外提供的写服务完全不停。那些新写入的命令会同时做两件事写入主库自身以及追加到一份被称为repl_backlog的环形缓冲区中。我打个比方这就像你在帮我打包一箱行李我人已经在赶高铁的路上了但你还通过手机持续告诉我“我把你的牙刷塞进去了、我在行李箱外侧又贴了一张标签”。等行李送到我还得根据这段时间收到的指令把后续动作全部补上。RDB快照对应“打包好的行李”repl_backlog对应“你在我赶路时持续给我的补充指令”。这套设计的精妙之处在于主库不必为了准备快照而停写从库也不必等快照传完才开始接触数据。快照往往要传几十秒甚至几分钟这段时间内的新命令只能暂存在backlog里等从库加载完RDB再一并补过去。2.4 RDB到达从库之后清空、落地、追平三连当RDB文件通过网络完整到达从库后真正的动作才开始。从库会执行以下一系列操作清空本地数据库从库会执行类似FLUSHALL的清空逻辑把本地已有的所有数据全部删掉。这一步是必须的否则旧数据会和RDB中的新数据掺杂在一起导致最终结果不正确。加载RDB把收到的RDB文件加载进内存。加载期间从库不会处理来自业务侧的普通请求这就意味着如果你在一台承担读流量的从库上触发全量同步这个从库在加载窗口内对客户端来说是“卡住”或“拒绝服务”的。追平增量RDB加载完成后从库会继续从主库的repl_backlog中拉取RDB生成之后产生的新命令并逐一执行直到它的offset追上主库当前的master_repl_offset。这三步做完从库才算是真正“追平”了主库从此刻起进入增量同步状态。注意这里的第二步从库加载RDB期间的不可服务状态往往被很多人在评估主从方案时忽略。如果你有3个从库同时需要重新同步主库的RDB可能会在极短时间内连续发给多个从库这会让多个从库同时进入加载状态整个读写链路都可能出现大面积超时。这也是我后来坚持“限制同时重连的从库数量、错峰重启”的原因。2.5 全量同步的隐性成本不止是带宽和CPU很多人衡量全量同步只看两点RDB有多大、网卡有多快。实际上它的隐性成本远不止这些。我整理一下我在实际环境里见过的几类问题fork阻塞触发BGSAVE会fork子进程fork瞬间主进程要复制虚存页表。实例内存越大fork耗时越长。极端情况下一个20GB内存的Redis实例fork可能耗时达到几百毫秒甚至秒级这期间主进程无法处理任何命令。写时复制导致的额外内存与IOfork之后主库写入的新页面会被复制一份内存会临时上涨同时磁盘要写整个RDB文件。如果原实例内存本来就在高位运行全量同步很容易把内存推到危险水位。网络叠加拥塞几十GB的RDB推送会让出方向带宽飙升如果主库的下游还有其他正常读取请求大家会一起挤占带宽造成整体访问延迟抬升。从库不可服务窗口从库加载RDB期间无法提供读服务如果业务层没有故障转移会导致读流量全部打到主库进一步加剧主库压力。这也是我为什么在事故之后采取了一套组合措施把repl-diskless-sync打开让RDB不必先写磁盘、把从库重启时间错开、并严格关注fork耗时的监控指标。这些措施在前面提到的事故场景里立竿见影。3. 增量同步的发动机replid、offset和repl_backlog3.1 replid一把验证复制身份的钥匙replid是一个40位的十六进制随机字符串它在主库每次启动时重新生成。你可以把它理解为主库这次生命周期里的“身份指纹”。从库第一次全量同步时会把主库的replid记下来之后每次发PSYNC都会带上它。主库收到PSYNC请求后第一个检查项就是这个从库声明的replid和我当前的replid是否匹配。这里有个很容易忽略的细节如果主库本身是从库提升上来的它可能会持有两个复制ID一个是自己当前作为主库的master_replid另一个是它曾经的复制流ID被称为replid2。Redis这样设计是为了在主从切换后尽量让老从库还能走增量同步。这个机制我在后面讲主从切换的章节会专门展开。在运维视角里info replication输出中两个replid字段的含义值得记一下master_replid:8e2a1f3c5d... master_replid2:000000000000...第一个是主库当前身份第二个是它上位之前继承的老身份。如果第二个全是0说明这台主库没有“前任”它的复制历史就是自己这条线。3.2 offset一份精确到字节的进度账本如果说replid回答的是“你是谁”offset回答的就是“你走到哪一步了”。在Redis的复制协议中每一次写命令都对应一段字节流主库每向从库发送一段命令自己的master_repl_offset就会增加对应字节数从库每执行完一段命令自己的slave_repl_offset也会增加同样的数量。通过对比这两个offset的差值可以判断从库的落后程度。比如主库当前offset是1002300从库的offset是1001300那就意味着从库还差1000字节的命令没有执行。这个差值除以主库的平均写入速率可以粗略估算出从库的延迟秒数。在增量同步中从库还会每秒向主库发送一次REPLCONF ACK offset用这个机制把自己的进度实时汇报给主库。主库拿到这个ACK有两个用途判断从库是否存活以及判断从库是否满足min-replicas-to-write这类策略的约束。3.3 repl_backlog环形缓冲区里的“临时记忆”增量同步能够成立依赖一个容易被忽视的基础设施repl_backlog。这个缓冲区存在于主库内存中默认大小只有1MB。主库每执行一次写命令都会把这个命令追加到repl_backlog中。这段话的另一个含义是增量同步并不是从库里蓄水池而是repl_backlog蓄水池。如果从库断线时间过久主库仍然在不停写入新命令repl_backlog中的旧命令就会一点点被覆盖。一旦从库落后的offset已经超出repl_backlog的保留范围主库就再也无法找回那段历史只能让从库做全量同步。我常用一个公式来估算backlog的合理性所需backlog大小 预期断线时长 × 每秒写量字节举个例子如果你的主库平均每秒写入2MB数据你希望它能容忍从库断线30秒后仍走增量同步那么backlog至少需要60MB。默认的1MB在这种写入速率下一个断线连半秒都撑不过。这也就是很多环境里“从库重启一下就全量”的直接原因。3.4 心跳与ACK增量同步的“体检系统”增量同步的过程中主从之间并不只是单纯的数据流动还有一套轻量的保活机制在持续运作。主库默认每10秒向从库发送一次PING由参数repl-ping-replica-period控制用来维持连接活性从库每秒向主库上报一次REPLCONF ACK同时携带自己当前的offset。主库通过比较最近一次收到ACK的时间来判断一个从库是否还活着。如果超过repl-timeout默认60秒没有收到从库的任何响应主库就会认为该从库已经挂掉主动断开连接。从库那边也一样如果它超过repl-timeout没有收到主库的任何数据也会主动断开并尝试重连。这里我想提醒一个参数联动关系repl-timeout必须大于repl-ping-replica-period否则可能出现“从库明明在线但因为PING还没发过来主库先把它超时踢掉”的误判。默认值是10秒和60秒这个差距通常没问题但如果你把repl-ping-replica-period调大到了30秒以上就一定要同步调大repl-timeout。4. 重连后的裁决什么时候能走增量什么时候只能全量4.1 PSYNC重连请求的两种可能应答当从库因为网络抖动、重启等原因断开后它会尝试重新与主库建立连接并发送形如PSYNC replid offset的请求。主库收到后会做两个关键判断从库携带的replid是否等值于主库当前的master_replid或replid2。从库声明的offset是否仍然落在repl_backlog保留的区间内。如果两个条件都满足主库返回CONTINUE replid offset这意味着从库不必重新拉全量主库会直接从它断掉的offset继续发送剩余的命令。只要任何一个条件不满足主库就会返回FULLRESYNC并强制从库做一次全量同步。对于运维来说这个裁决逻辑非常重要因为表面上看起来只是“网络又断了一下”实际触发的可能是几十GB的全量传输。4.2 backlog太小是最常见的“全量重同步”触发源我在很多团队里看到的状况是Redis实例部署都快两年了谁也没去动过repl-backlog-size这个参数大家默认它还是Redis出厂时的1MB。等到某天从库重启主库这期间只要写入的数据量超过1MB重连的时候offset就从backlog里找不到了于是触发全量同步。我们来做一个简单的推算。假设主库每秒写入5MB从库断线10秒后重连那么backlog里需要至少保留50MB的数据才能让它是增量。而默认配置只有1MB远不够用。你可以这样反向估算断线时长 repl-backlog-size / 每秒写入数据量默认1MB配置下如果你的主库每秒写1MB你最多只能容忍断线1秒每秒写100KB才能容忍10秒。大多数生产环境的写入量都远不止这些所以把backlog视情况调大是很有必要的。我在实际项目中使用的经验值是先统计主库高峰时段的写入速率再乘以一个期望容忍的断线时长我一般按5到10分钟算得出的结果至少要比这个值大一倍。比如主库高峰期每秒写2MB我期望容忍10分钟断线那么backlog至少应该是2MB × 600s 1200MB实际配置我可能会给到2GB。4.3 主从切换后replid2是如何挽救一场全量同步的Redis 4.0之前主从切换场景一直有个老大难问题从库原本跟着老主库同步老主库挂了哨兵把某个从库提升为主库。这个新主库一启动生成的是全新的replid其他从库带着老主库的replid来请求时必然不匹配结果所有从库都不得不做全量同步。Redis 4.0引入的PSYNC2机制解决了这个问题。当一台从库被提升为主库时它不会抛弃自己原来的复制身份而是会把老主库的replid保存为replid2。这样其他从库重连时带着老主库的replid来新主库发现它与自己的replid2匹配同时offset又在自己的backlog范围内就仍然可以走增量同步。这个设计在实际故障转移中价值巨大能避免“切换一次主库、全群从库重新全量”的雪崩发生。不过你也要知道它的边界如果其他从库落后太多offset已经超出新主库的backlog保留区间照样无法增量只能全量。这也是我为什么在主从切换场景下会提前把backlog临时调大的原因——不是为了切换本身而是为了给切换后的重连留足“缓冲余地”。4.4 容易被忽略的backlog回收机制还有一个细节很多人不知道主库的repl_backlog并不是一旦开启就永远存在。当主库一个从库都没有连接时经过repl-backlog-ttl默认3600秒的等待时间后Redis会自动释放掉这个缓冲区以节省内存。如果某个老从库在backlog释放之后才重连即便它带着完全匹配的replid主库也拿不出那段命令流一样只能全量同步。这个场景很隐蔽。比如你有一台从库停机维护了很久超过1小时而这台主库又是一个常年没有其他从库连接的状态那么backlog很可能已经被释放。等从库回来想走增量主库根本找不到它的历史只能老老实实全量。遇到这种情况不需要焦虑——它说明的不是Redis的缺陷而是所有“增量能力”都有存储边界。5. 线上主从同步问题的排查链路5.1 同步延迟一直涨先分清是写入快、网络差还是从库卡最直观的排查方式就是对比主库和从库的offset。在从库上执行redis-cli info replication看slave_repl_offset在主库上看master_repl_offset两者之差就是积压未同步的字节数。如果差值在持续增长我建议按这个顺序排查看主库的写入吞吐。可以用redis-cli info stats里的instantaneous_ops_per_sec结合master_repl_offset的变化速度判断当前写压力有多大。看主从之间的网络质量。在从库上用redis-cli --latency -h 主库IP测一下往返延迟如果延迟过高说明是网络层面的问题。看从库是否在执行慢操作。比如从库此时正在做过期键扫描、正在被客户端执行一些复杂查询都可能拖慢它处理复制流的速度。从库上执行redis-cli info commandstats能看出耗时最多的命令。看是否触发了client-output-buffer-limit replica限制。主库为从库维护的发送缓冲区如果超限会直接断开从库连接让你看到“连接反复断开重连”的现象。绝大多数延迟问题的真相最终都会落在这四类原因里。5.2 全量同步把主库拖垮无盘复制与级联方案回到文章开头那个事故从库重启触发了全量同步主库要先把RDB写到磁盘再读磁盘发送。整个过程里磁盘和带宽被双重占用主库写入基本被拖垮。对于这种场景repl-diskless-sync就是专门对症的方案。开启这个参数后主库不再把RDB写入磁盘文件而是让子进程直接把RDB数据流式发送给从库。这样能省掉写盘再读盘的两次IO尤其适合磁盘性能差、网络带宽充裕的环境。但无盘复制也有自己的适用前提它要求主库和从库之间的网络质量非常好否则几百MB甚至几十GB的RDB在网络上边生成边发送一旦中途卡顿子进程就会被拖住内存中保留的写时复制页面也会一直占着。Redis 6.0之后还支持无盘加载repl-diskless-load让从库在接收RDB时可以直接流式写入内存不必先落盘进一步减少加载时间。但这些都是“改善型”手段真正的长期解法是尽量避免全量同步频繁发生。还有一条思路值得一试如果主库下面挂了很多从库不要让所有从库都直接连主库而是采用“级联复制”的结构。让其中一个从库成为二级主库其他从库连它这样主库只需承担一条全量同步链路的压力而不会同时向多个从库推数据。代价是从库之间的延迟会比直连主库稍高一点适合对一致性要求不苛刻的场景。5.3 从库数据与主库不一致一个容易被忽略的配置从库默认是只读的参数是slave-read-only yes。如果你为了某些特殊需求把它改成了no允许业务直接写从库那么一旦之后发生全量同步从库上那些本地写入的数据会被RDB快照覆盖最终“消失”。很多数据不一致的事故根源就在这个开关上。除了这种人为写入还有一个和过期键相关的点主库在删除某个键时会向从库传播一条DEL命令但如果主库还没有删除这个键从库上却已经因为惰性过期而“看不到”这个键了从库并不会主动删除它。这就会造成主库内存里没有这个键从库内存里还占着它的不致命但令人困惑的现象。如果你排查“为什么主从内存差这么多”这可能是原因之一。不过正常业务下这个差异会随着主库删除键后传播DEL而慢慢被纠正。所以我的建议是从库永远保持只读任何写操作都不要落到从库上。如果你需要从库承担临时任务也尽量用可丢弃的独立副本不要在主从架构中让从库变成一个“写入口”。5.4 排查主从同步时我最常用的命令组合如果你手头没有现成的监控系统这几条命令已经能覆盖大部分排查场景# 在主库上看从库连接状态、replid、offset、backlog情况 redis-cli -p 6379 info replication # 在从库上看主库连接状态、同步截止位置 redis-cli -p 6380 info replication | grep -E master_link|master_replid|slave_repl_offset # 看主库fork耗时单位微秒全量同步是否引发阻塞 redis-cli -p 6379 info stats | grep fork # 看主库的读写速率和瞬时ops redis-cli -p 6379 info stats | grep -E instantaneous_ops_per_sec|total_net_input_bytes # 看从库上是否有慢命令 redis-cli -p 6380 slowlog get 20把这五条命令的执行结果串起来读基本就能定位到延迟是发生在传输阶段、加载阶段还是命令执行阶段。6. 主从同步相关参数取舍与调优实操6.1 核心参数一览与我的推荐配置主从同步涉及的参数不算多但每一个参数都直接影响了“什么时候走全量”这个核心命题。我把实际环境里最值得关注的几个列成表格并附上我个人的倾向值参数默认值作用我的推荐repl-backlog-size1mb主库缓冲区大小决定断线后可增量的最大数据量按“峰值写速率 × 容忍断线时长”估算后再放大一倍repl-backlog-ttl3600s无从库连接时缓冲区的保留时长保持默认即可除非你内存极紧张repl-timeout60s主从之间判定超时的时间上限保持默认如果调整了PING周期则要联动repl-ping-replica-period10s主库向从库发送PING的周期保持默认repl-diskless-syncno是否无盘复制直接流式传输RDB磁盘慢、网络好时开启client-output-buffer-limit replica256mb 64mb 60从库输出缓冲区限制从库消费能力偏弱时适当调大硬限制min-replicas-to-write0当可用从库数低于阈值时主库拒绝写高可用场景建议开启min-replicas-max-lag10s配合上一个参数判断从库是否“可用”按业务可接受延迟设置需要注意的是我把repl-backlog-size放在了第一位。不是因为别的而是因为它是决定“重连后是否全量”的权重最高的参数然而在所有复制参数里它恰恰是最容易被大家忽视的一个。6.2 参数之间的连锁反应调整之前先想清楚关系主从同步参数不是孤立起作用的它们之间存在很强的联动关系。举三个例子repl-timeout和repl-ping-replica-periodPING是主库向从库发心跳的间隔timeout是判定超时的上限。如果你把PING周期调整得过长却忘了同步调整timeout可能在网络轻微抖动时触发误判。timeout应该至少是PING周期的两倍以上最稳妥的是保持默认组合。repl-backlog-size和client-output-buffer-limit replicabacklog解决的是“主库还记得多少历史”output buffer限制解决的是“主库发送缓冲区能积压多少数据”。如果一个从库消费能力很弱即使backlog里还有数据主库也可能因为给从库发送的缓冲区超限而主动断开它重连后又是全量。所以遇到“频繁断线重连”的问题不能只看backlog还要看output buffer相关日志。min-replicas-to-write和min-replicas-max-lag这两个参数是主库自我保护机制。只有当在线从库数不少于阈值、且所有在线从库的滞后时间不超过最大lag时主库才接受写请求。如果你开了这个功能一定要把lag阈值设置得大于日常可能出现的延迟抖动否则主库会间歇性拒绝写入。6.3 我建议的日常监控视角不花哨但能救命最后聊聊监控。主从同步的监控不需要多复杂的系统有几个视角是必须长期盯住的从库数量与状态主库上connected_slaves数量是否稳定有没有从库长时间处于sync状态。offset差值最直接反映延迟的指标。可以写一个简单的定时任务每30秒采集一次主库和从库的offset差值超过阈值就告警。fork耗时info stats中的latest_fork_usec如果这个值持续偏高说明实例内存很大一旦触发全量同步主库会面临显著停顿风险。backlog覆盖情况观察repl_backlog_first_byte_offset和master_repl_offset之间的差值是否接近repl_backlog_size。如果经常逼近上限说明backlog配小了下一次断线重连很容易全量。这四类指标配合每次事故后的“全量触发原因复盘”基本能把主从同步的健康状态长期控制在可控范围。最后再分享一点我个人的体会。主从同步里所有所谓的“坑”归根结底都是一个平衡题内存换容错带宽换恢复速度配置换稳定性。你不需要把每个参数都调到极致但一定要让参数与你的真实写入量、断线频次、网络质量匹配。每次我遇到主从同步问题都会先问自己一句这次全量同步是否真的无法避免如果答案是否定的那我首先要做的一定是把backlog调大、把重连节奏错开让增量同步尽量成为常态选择。这才是主从同步最值得花心思的地方。
返回列表