主库写了备库查不到?主从延迟四步定位法
十五年数据库相关经验,做过 DBA、架构师、技术顾问。不求"颠覆",只求"靠谱"。
主从同步这个事,看起来简单。主库写、备库读,binlog 传过去、relay log 回放完,齐活。
但真出问题时能要人命。
上个月接到一个告警,业务方反馈"刚下的订单查不到"。排查下来是主从延迟 30 秒——用户刚在主库写完,备库还没同步到。30 秒在互联网业务里就是"数据丢了"。
干了这么多年 DBA,主从延迟是最容易被低估、也最容易背锅的问题。今天把排查方法论整理出来,从"发现延迟"到"定位根因"到"解决",每一步都说清楚。
有好消息,也有踩坑的地方。各位耐心看完。
01 延迟从哪来?先搞清主从同步的完整链路
排查延迟之前,先搞清楚延迟可能出现在哪个环节。
主从同步不是"主库写完备库立刻就有",它是一整条链路:
第一步:主库写 binlog。主库执行写操作后,把变更记录写入 binlog 文件。这一步通常很快,毫秒级。
第二步:binlog 传输到备库。备库的 I/O 线程连接主库,拉取 binlog,写入备库的 relay log。这一步的延迟取决于网络带宽和 binlog 产生速度。
第三步:备库回放 relay log。备库的 SQL 线程(或者多线程复制的工作线程)读取 relay log,把变更重放到备库数据上。这一步是延迟的重灾区。
第四步:备库数据可见。回放完成后,新数据才能在备库上被查询到。
关键认知:主从延迟不是"一个数字",是整条链路累积的结果。排查时要逐段确认延迟出在哪个环节。
02 怎么发现延迟?别等业务方来报
主从延迟最可怕的不是"有延迟",是"有延迟但没人知道"。
等业务方反馈"刚写的数据查不到",问题已经发生很久了。DBA 应该在业务感知之前就发现并处理。
监控指标:看这三个数就够了
MySQL 8.0+:查询performance_schema.replication_connection_status和replication_applier_status,关注LAST_HEARTBEAT_TIMESTAMP(最后一次心跳时间)和SERVICE_STATE(复制状态)。
MySQL 5.7:执行SHOW SLAVE STATUS,关注Seconds_Behind_Master(延迟秒数)和Slave_IO_Running/Slave_SQL_Running(复制线程状态)。
PostgreSQL:查询pg_stat_replication,关注write_lag、flush_lag、replay_lag(分别对应写入、刷盘、回放延迟)。
三个阈值,分三级告警
| 延迟级别 | 阈值 | 响应动作 |
|---|---|---|
| 正常 | < 1 秒 | 无需处理,常规监控 |
| 警告 | 1-10 秒 | 关注趋势,准备介入 |
| 危险 | > 10 秒 | 立即排查,必要时切主 |
我的习惯:告警不要只设一个阈值。设三级,让团队知道"现在是什么状态"。只设一个"延迟 > 10 秒告警",等告警响了再查,黄花菜都凉了。
03 排查流程——四步定位法
发现延迟后,按这个流程走,90% 的主从延迟都能定位到根因。
第一步:确认延迟在主库还是备库
怎么看?
在主库执行一个写入操作,记录时间戳。然后在备库查询这条数据,记录看到的时间戳。两者之差就是端到端延迟。
如果延迟很大,但主库的 binlog 写入很快(查 binlog 文件大小增长速度),说明问题不在主库,在传输或回放环节。
第二步:确认是传输慢还是回放慢
怎么看?
MySQL 执行SHOW SLAVE STATUS,对比两个值:
Read_Master_Log_Pos:I/O 线程读到的主库 binlog 位置Exec_Master_Log_Pos:SQL 线程回放到的位置
如果Read_Master_Log_Pos落后Master_Log_Pos(主库当前位置)很多,说明传输慢——网络带宽不够或者主库 binlog 产生太快。
如果Read_Master_Log_Pos跟得上,但Exec_Master_Log_Pos落后很多,说明回放慢——备库 SQL 线程处理不过来。
第三步:定位回放慢的具体原因
回放慢是最常见的延迟原因。具体又分几种情况:
情况 A:大事务回放。主库执行了一个大事务(比如批量更新了 100 万行),备库回放这个事务时,SQL 线程被占住,其他小事务都得排队等。
怎么看?查SHOW PROCESSLIST,看备库 SQL 线程正在执行什么 SQL。如果是一条执行了几十秒还没完的 UPDATE 或 DELETE,基本就是大事务了。
情况 B:锁冲突。备库回放时遇到锁冲突,SQL 线程被阻塞。
怎么看?查备库的information_schema.innodb_trx和data_lock_waits,看有没有锁等待。
情况 C:备库硬件跟不上。主库用 SSD,备库用的是机械盘。同样的写入量,备库回放速度天然就慢。
怎么看?对比主备两端的磁盘 I/O 指标(iostat 看 iowait 和吞吐量)。如果备库磁盘利用率持续 100%,就是硬件瓶颈。
第四步:验证根因
定位到可能的根因后,验证一下:
- 如果是大事务,在测试环境模拟同样规模的事务,看备库回放耗时。
- 如果是锁冲突,分析锁等待链,确认阻塞源。
- 如果是硬件瓶颈,做磁盘 I/O 压测,确认备库磁盘确实扛不住。
经验:不要猜根因,要验证。我见过太多 DBA 凭经验"觉得"是大事务导致的,结果查半天发现是备库磁盘满了。
04 解决思路——对症下药
定位到根因后,解决思路就清晰了。
方案 A:大事务 → 拆分事务 + 并行复制
大事务是主从延迟的头号杀手。一个事务更新了 100 万行,备库 SQL 线程要一条一条回放,可能花几十分钟。
治标:把大事务拆成小事务。原来一个 UPDATE 更新 100 万行,改成每次更新 1 万行,分 100 次执行。每次持锁时间短,备库回放也快。
治本:开启并行复制。MySQL 5.7+ 支持多线程复制(MTS),备库可以用多个线程并行回放不同库或不同事务的变更。
-- MySQL 开启并行复制(基于库的并行)SETGLOBALslave_parallel_workers=4;-- MySQL 5.7+ 基于逻辑时钟的并行复制(推荐)SETGLOBALslave_parallel_type='LOGICAL_CLOCK';SETGLOBALslave_parallel_workers=8;注意:并行复制不是线程越多越好。线程数超过 CPU 核心数后,收益递减。一般设成 CPU 核心数的 1-2 倍。
方案 B:网络传输慢 → 压缩 + 带宽扩容
如果确认是 binlog 传输慢,两个方向解决:
开启 binlog 压缩:MySQL 5.7+ 支持 binlog 传输压缩,在网络带宽紧张时能显著降低传输量。
-- 主库开启 binlog 压缩SETGLOBALbinlog_transaction_dependency_tracking='WRITESET';扩容网络带宽:如果主备跨机房部署,网络带宽是硬瓶颈。该升级就升级,别省这个钱。
方案 C:备库硬件瓶颈 → 升级存储 + 读写分离
如果备库硬件确实扛不住,两条路:
短期:把备库的读流量切一部分走主库,降低备库负载。
长期:升级备库存储。把机械盘换成 SSD,或者上 NVMe。硬件升级的钱,比延迟导致业务损失的钱少多了。
对比:三种复制方案的延迟表现
把上面的内容做个对比,方便选型:
| 方案 | 延迟范围 | 适用场景 | 局限性 |
|---|---|---|---|
| 单线程复制 | 秒级到分钟级 | 小数据量、低写入频率 | 大事务必延迟 |
| 基于库的并行复制 | 亚秒级到秒级 | 多库部署、写入分散 | 单库大事务仍然慢 |
| 基于逻辑时钟的并行复制 | 亚秒级 | 生产环境推荐 | 需要 MySQL 5.7+ |
| 半同步复制 | 毫秒级(但有写入延迟) | 数据零丢失场景 | 主库写入变慢 |
选型建议:生产环境优先用基于逻辑时钟的并行复制(MTS)。数据安全性要求极高的场景,用半同步复制,但接受主库写入性能的轻微下降。
延迟容忍度的判断框架
不是所有场景都需要"零延迟"。按业务容忍度分三档:
容忍度高(延迟 < 10 秒可接受):
- 后台报表查询
- 数据分析任务
- 非实时用户查询
- 方案:标准异步复制 + 常规监控
容忍度中(延迟 < 3 秒可接受):
- 用户个人中心查询
- 商品详情查询
- 订单状态查询
- 方案:并行复制 + 三级告警 + 自动切换
容忍度低(延迟 < 1 秒可接受):
- 账户余额查询
- 实时库存查询
- 支付状态确认
- 方案:半同步复制 + 强制走主库查询 + 秒级监控
我的经验:不要一刀切要求"主从零延迟"。不同业务场景对延迟的容忍度不同。按场景分级治理,比统一高标准更务实。
从"主从延迟是玄学"到"延迟是可管理的"
说实话,干 DBA 前五年,我对主从延迟的态度是"能忍就忍"。延迟几秒嘛,业务又不会挂。
后来接了几个金融项目才明白:延迟不是"能不能忍"的问题,是"业务允不允许"的问题。证券交易系统,延迟 1 秒就是"数据不一致",合规上过不了关。
这个认知转变让我重新审视主从延迟的排查方法。以前是"延迟大了再看",现在是"持续监控、分级治理、提前预警"。
主从延迟不是玄学,是可测量、可定位、可优化的工程问题。
深度分析:为什么并行复制不能解决所有延迟问题?
很多人以为"开了并行复制就万事大吉"。实际不是这样。
并行复制解决的是"多个小事务排队等"的问题。但如果来了一个大事务(比如批量删除 1000 万条过期日志),再多的并行线程也得等这个事务回放完。
根因在于:大事务本身是一个不可分割的原子操作。备库不能把一个事务拆成两半来回放——那会破坏事务的原子性。
所以并行复制有天花板。它能解决并发小事务的延迟,解决不了单一大事务的延迟。
真正的解法是:从源头控制事务大小。应用层做批量操作时,分批提交。每批 1000-5000 行,不要一个事务搞定。
这个道理和 DBA 的日常建议一致:事务尽量短。不仅是为了减少锁等待,也是为了减少主从延迟。
主从延迟巡检清单
日常巡检建议每周执行一次:
1. 延迟趋势检查
- 查看过去一周的延迟曲线,确认没有持续增长趋势
- 如果延迟峰值在上升,说明复制能力在下降,需要排查
2. 复制线程状态
- 确认 I/O 线程和 SQL 线程都在运行
- 如果有线程停过,查错误日志看原因
3. 大事务监控
- 查看主库 binlog 中事务大小的分布
- 如果出现异常大的事务,通知应用团队优化
4. 硬件资源检查
- 备库磁盘 I/O 利用率是否接近上限
- 备库 CPU 和内存使用是否正常
5. 告警规则验证
- 确认三级告警阈值配置正确
- 测试告警通道是否正常(发一条测试告警确认能收到)
总结
主从延迟排查就一套流程:看监控 → 分段定位 → 验证根因 → 对症下药。
核心认知:延迟不是"一个数字",是整条链路累积的结果。传输慢和回放慢是两回事,不能混为一谈。
预防延迟的关键:事务要短、复制要并行、监控要分级。
处理过几十次主从延迟问题,每次回到这套流程,问题都能解决。不需要什么高级工具,耐心和方法论就够了。
后续我会继续分享数据库参数调优实战、容量规划方法论这些话题,跟着我一篇篇学,数据库这块就没问题了。
有问题评论区见。
十五年数据库领域老炮。关注我,一起把数据库这件事搞明白。
