高并发下MySQL写入吞吐不降反升——组提交的三阶段原理与调优参数

0 阅读5分钟

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

先问一个问题。

你觉得数据库的写入吞吐,是随着并发量增加而线性增长,还是到某个点之后就上不去了?

大多数人的直觉是:并发越高,磁盘I/O压力越大,写入吞吐迟早会撞到天花板。

但MySQL的表现恰恰相反。在并发量增加到一定程度后,写入吞吐不但没有下降,反而还在上升。原因就在组提交(Group Commit) ——它把多个事务的刷盘操作合并成一次,并发越高,合并的机会越多,单次刷盘的效率就越高。

今天把Redo Log的刷盘机制和组提交彻底拆开讲清楚。

先搞懂几个词:

Redo Log:InnoDB的物理日志,记录“在某个数据页上做了什么修改”。事务提交时,先写Redo Log,再修改内存中的数据页。即使数据库崩溃,重启后也能通过Redo Log恢复数据。

WAL(Write-Ahead Logging) :预写日志。先写日志,再写数据。Redo Log就是WAL机制的实现。

LSN(Log Sequence Number) :日志序列号,单调递增,标识Redo Log中的每个位置。

fsync:将操作系统缓存中的数据强制刷到磁盘。这是一个昂贵的操作,组提交优化的就是它。

组提交(Group Commit) :将多个事务的Redo Log刷盘操作合并为一次fsync,减少磁盘I/O次数。

一、Redo Log的物理结构

Redo Log在磁盘上由多个文件组成,默认是两个ib_logfile文件。这些文件被组织成log group,内部细分为log block(默认512字节)。

Redo Log是循环写的。写满之后,从头部重新开始覆盖。但覆盖的前提是:被覆盖的日志对应的脏页已经刷盘。这就是Checkpoint的机制——Checkpoint的位置决定了哪些Redo Log可以安全覆盖。

如果数据库写入量太大,Redo Log写满但脏页还没刷完,InnoDB会暂停所有写入,等待脏页刷盘。这就是“Checkpoint风暴”,生产环境需要监控Innodb_log_waits来预警。

二、innodb_flush_log_at_trx_commit的三种值

这个参数控制事务提交时Redo Log的刷盘时机,是持久化和性能之间最核心的权衡。

值行为数据安全性性能
0事务提交时不刷盘,由后台线程每秒刷一次崩溃可能丢1秒数据最高
1每次事务提交都刷盘(fsync)不丢数据最低
2事务提交时写入OS缓存,由OS决定何时刷盘数据库崩溃不丢,OS崩溃丢1秒中等

值=1是金融级系统的标配,但很多人以为它性能一定很差。其实在高并发下,组提交让值=1的性能并没有想象中那么差。

三、组提交的三阶段流程

组提交的核心思想是:把多个事务的刷盘操作攒在一起,一次fsync搞定。

MySQL的组提交分三个阶段:

阶段一:Flush阶段(写Redo Log到OS缓存)

多个事务的Redo Log按顺序写入Redo Log Buffer,然后由第一个到达的事务(Leader)将这批日志一次性写入OS缓存(write系统调用)。其他事务(Follower)等待。

阶段二:Sync阶段(fsync到磁盘)

Leader执行fsync,将OS缓存中的Redo Log刷到磁盘。Follower等待。

阶段三:Commit阶段(提交事务)

Leader和Follower依次提交事务,释放锁,返回客户端。

关键点:Flush阶段和Sync阶段都是“攒一批”再操作。并发越高,攒的批次越大,单次fsync覆盖的事务越多,均摊到每个事务的刷盘成本就越低。

这就是为什么高并发下写入吞吐反而更高的原因。

四、组提交的调优参数

MySQL提供了两个参数来控制组提交的等待策略:

binlog_group_commit_sync_delay

单位是微秒(默认0)。设置一个延迟值,让Leader在Sync阶段之前等一会儿,看看有没有更多事务加入批次。

比如设置为1000(1毫秒),Leader会等待1毫秒,让更多事务进入同一批次。代价是每个事务的提交延迟增加了1毫秒,但fsync次数减少,吞吐量提升。

binlog_group_commit_sync_no_delay_count

设置一个事务数量阈值。当等待的事务数达到这个值时,Leader不再等待,立即执行Sync。这是对binlog_group_commit_sync_delay的补充——避免等待时间过长。

调优建议:

  • 高并发、对延迟容忍度较高的场景:sync_delay设为500-1000微秒,no_delay_count设为10-20

  • 低延迟要求的场景:保持默认值0

  • 金融核心交易:保持值=1,sync_delay设为0或极小值,保证数据安全优先

五、监控组提交的效果

SHOW STATUS LIKE 'Binlog_commits';
SHOW STATUS LIKE 'Binlog_group_commits';

Binlog_group_commits / Binlog_commits的比值反映了组提交的合并效率。比值越接近1,说明每个批次包含的事务越多,合并效率越高。

如果比值接近1:1,说明每个事务都在单独刷盘,组提交没有发挥作用——可能是并发太低,或者sync_delay设置太小。

六、小结

Redo Log的刷盘机制是InnoDB持久化的核心。innodb_flush_log_at_trx_commit控制刷盘时机,组提交通过三阶段流程将多个事务的fsync合并为一次。高并发下组提交的合并效率更高,这就是为什么写入吞吐不降反升。binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count是调优组提交的关键参数,需要根据业务对延迟的容忍度来权衡。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~