事务提交慢八成卡在fsync上,需用strace盯mysqld线程是否长时间停在fsync(3),结合SHOW ENGINE INNODB STATUS中Log sequence number与Log flushed up to差距拉大、Innodb_os_log_pending_fsyncs非零持续上升、iostat的%util>80%且await>10ms、iotop确认mysqld大量刷盘等多维度证据综合判定。

事务提交慢,八成卡在 fsync 上,不是 SQL 写得差,也不是连接池配置低,先盯日志刷盘和磁盘 IO,再查锁和只读状态。
怎么确认卡在 fsync 而不是 SQL 执行本身
别急着改配置,先抓证据。MySQL 默认 innodb_flush_log_at_trx_commit = 1,每次 COMMIT 都要调一次 fsync(3);如果磁盘响应不过来,就卡在这儿。
- 用
strace -p $(pgrep mysqld | head -1) -e trace=fsync盯一个卡住的线程,看它是否长时间停在fsync(3)调用上 - 查
SHOW ENGINE INNODB STATUS\G的 LOG 段:Log sequence number和Log flushed up to差距持续拉大(比如超百 MB),说明日志刷盘跟不上 - 监控状态变量
Innodb_os_log_pending_fsyncs:非零且持续上升,就是铁证
iostat 和 iotop 怎么看才算真瓶颈
只看吞吐量会误判。MySQL 提交慢多是随机小 IO(4KB–16KB)拖垮,不是带宽不够。
-
iostat -x 1重点盯两个指标:%util > 80%(设备饱和)、await > 10ms(单次 IO 等待过长),两者同时出现且持续,基本坐实磁盘瓶颈 -
iotop -oP确认是不是mysqld进程在大量刷盘——尤其注意 WRITE 列里mysqld的 IO 占比是否主导 - 云盘场景(如阿里云 ESSD、AWS gp3)必须查 IOPS 配额是否耗尽;
iotop -p $(pgrep mysqld)还能看到实际发出的写请求大小
为什么 innodb_log_file_size 太小也会拖慢 COMMIT
日志文件小,InnoDB 被迫高频 checkpoint,把本该后台做的刷脏页动作推到前台事务里,表现为“每批提交突然卡一下”。
- 估算依据:按峰值写入量算,至少容纳 1–2 分钟的
Innodb_os_log_written增速(单位字节/秒) - 不能在线改:必须停库 → 删除
ib_logfile0和ib_logfile1→ 修改配置 → 重启;否则启动失败报InnoDB: Error: log file ./ib_logfile0 is of different size -
innodb_log_files_in_group只影响文件个数,不改变总容量;真正起作用的是innodb_log_file_size × innodb_log_files_in_group
别漏掉只读模式、磁盘满和长事务这三类“假慢”
这些情况会让事务看起来“卡住”,但根本不是性能问题,而是状态异常或资源耗尽。
- 检查只读开关:
SELECT @@read_only, @@super_read_only;;两者都为0才能写;云 RDS 控制台“只读实例”开关可能静默设置,改配置文件无效 - 磁盘空间或挂载只读:
df -h和mount | grep $(mysql --help | grep "datadir" | awk '{print $NF}')必须都正常;ERROR 1030 (HY000): Got error 28 from storage engine就是ENOSPC - 长事务未提交:
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id FROM information_schema.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 120;;trx_query为空 +trx_state = 'RUNNING',大概率是 BEGIN 后忘了 COMMIT
真正难的不是定位某一个点,而是区分“慢在哪儿”和“为什么慢”——比如 await 高,可能是云盘配额打满,也可能是 redo 日志路径落在 NFS 上;KILL 一个长事务前,得确认它没在跑真实业务逻辑,否则回滚本身又卡几分钟。


















