fsync是吞吐硬瓶颈,非慢而是必须等待;innodb_flush_log_at_trx_commit=1时组提交常失效,因“双1”导致二阶段提交串行化,Binlog_group_commit_trigger_count长期为0或1即为证据。

fsync 是吞吐量的硬瓶颈,不是“慢”,是“必须等”
MySQL 执行 COMMIT 时是否触发 fsync(),直接决定线程是否卡在磁盘 IO 上。一次 fsync() 在普通 NVMe SSD 上平均耗时 0.5~2ms,在云盘或高负载设备上可能达 5~10ms。这不是 CPU 计算延迟,而是不可压缩的物理等待——线程只能挂起,无法并行推进。每秒 1000 次事务提交,若每次都要 fsync,光日志刷盘就吃掉 1~2 秒 CPU 时间,吞吐自然被死死压住。
innodb_flush_log_at_trx_commit=1 时,组提交(Group Commit)不一定生效
很多人误以为设成 1 就“自动优化了”,其实不然。组提交要起效,得满足两个前提:
-
sync_binlog = 1且innodb_flush_log_at_trx_commit = 2—— 这是真正能打包刷盘的黄金组合; - 若两者都为 1(即所谓“双1”),二阶段提交会强制串行化:
prepare和commit阶段无法重叠,leader 线程刷完 redo,follower 必须等 binlog 也刷完才能继续,组提交退化为单事务排队。
实测中,Binlog_group_commit_trigger_count 在“双1”下长期为 0 或 1,就是组提交失效的明确信号。
值为 0 和 2 的刷盘时机差异,决定了崩溃后丢多少
别只看吞吐数字,得清楚丢什么、在哪丢:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
-
innodb_flush_log_at_trx_commit = 0:事务提交时log buffer完全不动,全靠后台每秒一次的write + fsync。MySQL 崩溃或整机断电,最多丢最近 1 秒内所有已COMMIT的事务; -
innodb_flush_log_at_trx_commit = 2:事务提交时至少完成write(进 OS page cache),后台每秒再fsync。MySQL 崩溃不丢数据,但 OS 崩溃或断电仍可能丢最后 1 秒日志; -
innodb_flush_log_at_trx_commit = 1:每次COMMIT都走完write + fsync,只要磁盘没物理损坏,数据就落盘了。
值为 0 看似快,但它把“丢失窗口”从“单事务级”扩大到“整秒级”,而且和 sync_binlog 协同不好时,还会引发主从不一致——binlog 已落盘而 redo 还在内存,crash 后备库多出记录,主库却没了。
真正影响吞吐的,往往是应用层事务粒度和驱动行为
调低 innodb_flush_log_at_trx_commit 只是开了个口子,能不能跑出高吞吐,还得看应用怎么用:
- 客户端驱动默认开启
autocommit=1?那哪怕设成 0,每条INSERT仍是独立事务,刷盘压力一点没减; - 批量插入写成
INSERT INTO ... VALUES (...), (...), (...)是基础,但若没用BEGIN/COMMIT包裹,数据库仍按单语句事务处理; - PyMySQL、mysql-connector-python 等驱动需显式启用 batch 模式,否则
executemany()内部仍拆成多次网络往返和事务提交; - 事务太大(如单事务插 50 万行)会撑爆
innodb_log_file_size,触发频繁 checkpoint,反而加剧 I/O 争抢。
所以压测时看到吞吐没提升,先查 SHOW ENGINE INNODB STATUS 里的 Log sequence number 和 Last checkpoint at 差值——如果长期 >70% 日志总容量,说明日志循环写被卡住,调参数也没用。










