大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!
先问一个问题。
你觉得数据库的写入吞吐,是随着并发量增加而线性增长,还是到某个点之后就上不去了?
大多数人的直觉是:并发越高,磁盘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 不愁
还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~