innodb_flush_log_at_trx_commit=1导致每次commit都触发fsync,引发io队列堆积、await升高、%util趋近100%,本质是高频小事务写入太“碎”而非数据量大;设为2可降为每秒一次fsync,提升吞吐但需容忍秒级数据丢失风险。

innodb_flush_log_at_trx_commit=1 会让每次 COMMIT 都卡在 fsync 上
这不是“写得慢”,而是“写得太碎”。每个小事务(比如单行 INSERT)提交时,MySQL 必须把 log buffer 中的 redo 日志强制刷到磁盘——调用的是 fsync() 或 fdatasync() 系统调用。在机械盘上一次 fsync 常耗时 3–10ms;SSD 好些,但也需 0.2–1ms。当 TPS 超过 200,IO 请求就排队堆积,iostat -x 里会看到 await 持续 >5ms、%util 接近 100%,但实际吞吐(wMB/s)可能只有 10–20MB/s。
常见错误现象:
-
SHOW PROCESSLIST大量连接卡在Updating或Committing -
Innodb_os_log_pending_fsyncs持续 > 0 -
Innodb_log_waits每秒增长,说明 log buffer 不够用、被迫等待刷盘
关键点:这个瓶颈和“数据量大小”无关,只和“提交频次”强相关。1000 个 1KB 的事务比 1 个 1MB 的事务更伤 IO 队列。
为什么设成 2 就能缓解,但不是所有场景都适用
innodb_flush_log_at_trx_commit=2 把同步动作从“每事务一次 fsync”降为“每秒一次 fsync”,本质是把随机小 IO 批量化。MySQL 进程崩溃不丢数据,OS 崩溃或断电最多丢 1 秒内未刷盘的 redo 日志。
使用条件必须明确:
- 业务能容忍秒级数据丢失(如埋点表、operation_log、实时统计中间表)
- 服务器有 UPS 或 RAID 卡带 BBU,避免意外断电直接击穿 OS cache
- 没开启
sync_binlog=1—— 否则两轮fsync叠加,性能更差 - 确认
innodb_log_file_size足够大(建议 ≥512MB),否则 checkpoint 频繁触发脏页刷盘,IO 压力从 redo 转移到 data file
别设成 0:MySQL 异常终止时整个 log buffer(默认 16MB)全丢,不可控;且无法满足大多数日志类业务的“至少保留最近几秒”的底线要求。
改完配置后最容易被忽略的三件事
参数生效 ≠ 性能恢复。很多人改了 innodb_flush_log_at_trx_commit 就以为完事,结果监控毫无变化。
-
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_fsyncs'必须从“每事务一次”变成“约每秒一次”(允许 ±10% 波动),否则说明没生效或应用还在用旧连接 - 老连接不会自动继承新值,需滚动重启应用或等连接池自然刷新;可查
information_schema.PROCESSLIST看当前连接的COMMAND和TIME,确认是否已重连 - 必须停库删除旧
ib_logfile*并重建,才能让更大的innodb_log_file_size生效;否则 MySQL 启动报错:Log file ib_logfile0 is of different size
真正卡住性能的,往往不是参数本身,而是改完之后没验证它是否真在按预期工作——尤其是 Innodb_os_log_fsyncs 这个指标,它比任何图表都诚实。











