确认磁盘IO拖慢:strace抓卡住线程看是否停在fsync(3),或查INNODB STATUS中Log sequence与Log flushed差距是否持续拉大;iostat -x 1观察await>20ms且%util≈100%。

事务提交卡在 fsync 上,怎么确认是磁盘 IO 拖慢的?
MySQL 提交事务时卡住,最直接的线索就是 innodb_flush_log_at_trx_commit=1(默认值)下,每次 COMMIT 都会触发一次 fsync 到 ib_logfile。如果磁盘慢,fsync 就成瓶颈。
验证方法很简单:用 strace 抓一个正在卡住的 mysqld 线程,看它是不是停在 fsync(3) 调用上;或者查 SHOW ENGINE INNODB STATUS 里的 LOG 部分,观察 Log sequence number 和 Log flushed up to 差距是否持续拉大。
-
pt-ioprofile或iostat -x 1查看%util和await,await > 20ms且%util ≈ 100%基本坐实磁盘瓶颈 - 别只看
SELECT延迟——写密集场景下,COMMIT才是真瓶颈,监控要盯住innodb_os_log_pending_fsyncs这个状态变量 - 云盘(如 AWS gp3、阿里云 ESSD)要注意吞吐和 IOPS 是否被配额限制,
iotop -p $(pgrep mysqld)可看到实际发出的写请求大小和频率
innodb_flush_log_at_trx_commit 改成 0 或 2 真的能提速?风险在哪?
能提速,但不是“随便改”。这个参数控制的是 redo log 刷盘时机,直接影响持久性语义:
-
0:每秒刷一次 log buffer 到文件,事务提交不刷盘 → 最快,但崩溃最多丢 1 秒数据 -
2:事务提交只写入 OS page cache,不fsync→ 通常比1快 3–10 倍,崩溃仅在 OS 崩溃时丢数据(MySQL 进程挂不影响) -
1:严格 ACID,但代价是每次COMMIT等fsync
线上调成 2 是常见折中方案,但注意:sync_binlog 如果也设为 0 或 1000,主从一致性可能出问题;若开启 GTID + 半同步,2 仍可满足大多数业务的可靠性要求。
除了改参数,还有哪些 IO 层面的硬优化手段?
参数调优只是表层,IO 效率取决于硬件路径是否通畅:
- 确保
innodb_log_file_size足够大(建议单个 ≥ 1G),避免频繁 checkpoint 导致 log 文件循环写放大 - redo log 文件必须放在独立物理盘(或至少独立 LVM 逻辑卷),千万别和
datadir共用同一块 SSD/NVMe - 禁用 ext4 的
barrier=1(已在较新内核默认关闭),但务必确认文件系统挂载用了data=ordered(默认)而非data=writeback - MySQL 8.0+ 可启用
innodb_redo_log_capacity动态调大总 redo 容量,减少日志归档压力
为什么 SSD 也卡?可能是 log_buffer 太小或日志写太碎
即使上了 NVMe,如果应用高频提交小事务(比如单行 INSERT + COMMIT),log buffer 写满快、fsync 触发频繁,依然会压垮设备 IOPS。
- 检查
innodb_log_buffer_size是否过小(默认 16M,高并发写建议 ≥ 64M) - 用
perf record -e block:block_rq_issue,block:block_rq_complete -p $(pgrep mysqld)看单次fsync对应多少 KB 的底层 IO 请求,若平均 - 业务层尽量合并事务(比如批量
INSERT后再COMMIT),比调参数更治本
磁盘 IO 慢从来不是孤立问题,它把应用模式、MySQL 参数、文件系统行为、硬件特性全串在一起。调一个 fsync 相关参数前,先搞清你到底在牺牲什么,又换来了什么——否则很可能刚压下去延迟,又冒出主从延迟或崩溃恢复失败。










