服务覆盖:昆明·曲靖·玉溪·保山·昭通·丽江·普洱·临沧·楚雄·红河·文山·西双版纳·大理·德宏·怒江·迪庆

MySQL主从延迟飙升到600秒,一个不起眼的参数救了命

eycit 2026-04-24 1 次阅读 网络故障
---

theme: default themeName: "默认主题" title: "MySQL主从延迟飙升到600秒,一个不起眼的参数救了命"


MySQL主从延迟飙升到600秒,一个不起眼的参数救了命

去年双十一前的压测,我们遇到了一个诡异的问题:MySQL主从延迟在高峰期飙升到600秒,读写分离基本废了。DBA团队查了两天没找到根因,最后是我从一个参数入手解决的。

这个问题太典型了,我敢说90%的MySQL用户都踩过或将会踩。今天把完整排查过程写出来,下次你遇到类似的坑,至少能省两天时间。

故障现象

先说现象:

  • 业务高峰期(QPS约8000),主从延迟从正常的0-1秒突然飙升
  • 延迟最高到600秒,Seconds_Behind_Master持续增长
  • 低峰期延迟自动恢复到0
  • 主库CPU、内存、IO都正常,从库资源也很空闲

乍一看像是从库性能问题,但从库资源完全没压力啊。这就很诡异了。

常见原因排查(全部排除)

按照标准流程,我依次排查了这些常见原因:

1. 从库硬件性能差?

-- 对比主从硬件配置

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_total';

主从都是32核64G的机器,SSD盘,硬件完全对等。排除。

2. 大事务导致延迟?

-- 查看当前正在执行的事务

SELECT * FROM information_schema.INNODB_TRX;

没有长事务。排除。

3. 从库单线程回放瓶颈?

SHOW VARIABLES LIKE 'slave_parallel_workers';

已经设置了8个并行worker。排除。

4. 网络抖动?

主从在同一机房,内网延迟0.1ms,用iperf测试带宽也够。排除。

5. binlog格式问题?

SHOW VARIABLES LIKE 'binlog_format';

已经是ROW格式,没问题。排除。

到这里,常规排查全部走完了,没找到原因。DBA团队就是在这一步卡住的。

突破口:看relay log的写入和回放速率

常规排查走不通,我换了个思路——不盯着"延迟"看,而是盯着"吞吐"看。

# 在从库上持续观察relay log的大小变化
watch -n 1 'ls -lh /var/lib/mysql/mysql-relay-bin.*tail -5'

发现一个有趣的现象:主库的binlog写入速度很快,但从库的relay log回放速度明显跟不上。具体来说:

  • 主库每秒产生约5MB的binlog
  • 从库每秒只能回放约2MB的relay log

回放速率只有写入速率的40%,难怪会延迟!

但问题又来了——从库已经开了8个并行worker,为什么回放还是这么慢?

深挖并行回放的瓶颈

关键来了。我查看了并行worker的实际工作状态:

-- 查看并行回放状态

SHOW PROCESSLIST;

发现8个worker中,大部分时间只有2-3个在干活,其他都在等。这说明并行度并没有真正发挥出来。

为什么?这就涉及到MySQL并行回放的一个重要机制——基于组提交的并行回放(MTS)

MySQL的并行回放有一个前提条件:只有同一个组提交(group commit)里的事务,才能并行回放。如果主库的组提交效率低,每次组提交里只有1-2个事务,那从库开再多的worker也没用。

查一下组提交的情况:

-- 查看组提交的统计

SHOW GLOBAL STATUS LIKE 'Binlog_group_commits%';

结果触目惊心:

Binlog_group_commits: 1583247

Binlog_group_commit_trigger_count: 0 Binlog_group_commit_trigger_timeout: 1583247

几乎所有的组提交都是靠timeout触发的! 也就是说,几乎每次组提交只包含1个事务。

这就是根因:主库的组提交效率极低,导致从库无法有效并行回放。

神奇的参数:binlog_group_commit_sync_delay

找到了根因,解决方案就呼之欲出了。MySQL提供了一个参数来优化组提交:

-- 增加组提交的等待时间,让更多事务合并到同一个组

SET GLOBAL binlog_group_commit_sync_delay = 1000; -- 等待1毫秒 SET GLOBAL binlog_group_commit_sync_no_delay_count = 10; -- 或者凑够10个事务

`binlog_group_commit_sync_delay` 的含义是:在刷盘前额外等待N微秒,让更多事务凑到一起提交。这个等待对主库的写入延迟影响微乎其微(1毫秒),但对组提交的效率提升巨大。

修改后的效果:

SHOW GLOBAL STATUS LIKE 'Binlog_group_commits%';
Binlog_group_commits: 892341

Binlog_group_commit_trigger_count: 654832 Binlog_group_commit_trigger_timeout: 237509

现在有73%的组提交是靠凑够事务数触发的,说明每次组提交包含了更多事务。

主从延迟从600秒降到了3秒以内。

参数调优的细节

这个参数不是越大越好。我测试了几个值:

参数值组提交效率主库写入延迟主从延迟
0(默认)极低最低600s
500μs一般几乎无感45s
1000μs较好几乎无感3s
5000μs很好略有感知0.5s
10000μs极好明显感知0s

最终我们选择了1000μs(1毫秒),在组提交效率和主库延迟之间取得了平衡。对你的业务来说,最优值可能不同,建议从1000开始测试。

还有一个配套参数值得调整:

-- 增加每次sync的binlog大小,减少fsync次数

SET GLOBAL sync_binlog = 100; -- 默认1,改为100

注意:`sync_binlog=100`意味着断电可能丢失最近100个事务的binlog。对于大多数业务来说可以接受,但金融场景要谨慎。

还有一个隐藏坑:从库的并行模式

光优化主库还不够,从库的并行回放模式也要调对:

-- 查看当前的并行模式

SHOW VARIABLES LIKE 'slave_parallel_type';

如果是`DATABASE`(默认值),改掉它:

SET GLOBAL slave_parallel_type = 'LOGICAL_CLOCK';

SET GLOBAL slave_parallel_workers = 8;

`DATABASE`模式只能对不同数据库的事务并行,对单库多表的场景毫无帮助。`LOGICAL_CLOCK`模式才能利用组提交信息实现真正的并行。

完整的优化配置

# my.cnf 优化项

[mysqld]

主库:提升组提交效率

binlog_group_commit_sync_delay = 1000 binlog_group_commit_sync_no_delay_count = 10 sync_binlog = 100

从库:提升并行回放效率

slave_parallel_type = LOGICAL_CLOCK slave_parallel_workers = 8 slave_preserve_commit_order = 1

修改后记得重启从库的复制线程:

STOP SLAVE;

START SLAVE;

事后反思

这次排查给我几个教训:

1. MySQL的主从延迟,很多时候根因在主库而不是从库。 我们花了两天盯着从库优化,方向都错了。

2. 并行不是万能的,要看并行度能不能真正发挥。 开了8个worker但只有2个在干活,问题不在worker数量,而在并行粒度。

3. 组提交是个被严重忽视的机制。 大多数MySQL调优文章都在讲索引、缓冲池,很少有人提组提交。但在主从架构下,它可能是影响最大的参数之一。

4. 压测要模拟真实场景。 我们的压测一直没问题,因为压测工具的事务都是单线程串行执行的,根本测不出组提交的问题。真实业务的并发模式完全不同。

5. 监控要覆盖组提交指标。 从那以后,我们把`Binlog_group_commits`相关指标加到了监控大盘上,再也没被这个问题打了个措手不及。

做IT这么多年,见过太多"早知道就好了"的情况。

希望这篇文章能帮你少走弯路。如果真的遇到问题,别一个人扛着——易云城IT服务随时待命。

📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com 🌐 https://www.eycit.com

您身边的IT专家。

上一篇
实战版:如何设置静态IP地址(家庭+企业网络)2026版...
下一篇
K8s集群升级翻车实录:从v1.28到v1.31的现血泪...