事务提交慢不消耗连接,但会阻塞连接归还;主因是半同步复制、redo log刷盘压力大或应用层未设超时,导致commit阶段连接长期占用,引发连接池打满。

事务提交慢直接卡住连接归还路径
事务提交慢本身不“吃”连接,但它让连接卡在 COMMIT 阶段无法返回池中。JDBC 驱动执行 connection.commit() 时会阻塞线程,直到 MySQL 返回确认;这期间连接一直被标记为“活跃”,连接池不会回收它。哪怕 SQL 执行只花 5ms,COMMIT 耗时 1.5s,那这个连接就被独占 1.5 秒——高并发下,几十个这样的请求就能把池子填满。
半同步复制是常见推手,尤其在流量高峰时
MySQL 默认的半同步复制要求主库至少等一个从库写完 relay log 才返回 COMMIT 成功。如果从库延迟、网络抖动或磁盘 IO 紧张,COMMIT 就会卡住。你看到接口 RT 突然在下午两点飙升,日志里 COMMIT 平均耗时 1.5s,大概率就是它。异步复制能立刻返回,但要接受主库宕机后少量数据丢失的风险。
- 检查当前模式:
SHOW VARIABLES LIKE 'rpl_semi_sync%'; - 临时切异步(需 DBA 配合):
SET GLOBAL rpl_semi_sync_master_enabled = OFF; - 长期方案:优化从库性能,或改用增强半同步(如 MGR)
Redo log 刷盘压力大也会拖慢 COMMIT
每次 COMMIT 都必须确保 redo log 持久化到磁盘(除非设了 innodb_flush_log_at_trx_commit=2)。如果 innodb_log_file_size 太小(比如只有 48MB),redo log 环形空间频繁写满,就会触发刷脏页 + 强制 fsync,导致 COMMIT 延迟。DBA 把它调到 1GB 后问题缓解,说明不是配置错,而是容量真不够。
- 查当前大小:
SHOW VARIABLES LIKE 'innodb_log_file_size'; - 估算合理值:按每秒事务量 × 平均事务日志量 × 2~3 秒缓冲来算
- 注意:调整需停库,且要和
innodb_log_files_in_group配合
应用层没设超时,让慢 COMMIT 彻底失控
Spring 的 @Transactional 默认无超时,一旦底层 COMMIT 卡住,整个 HTTP 请求线程就挂在那里,既不释放连接,也不响应客户端。连接池里的连接就这样一个个“静默失联”。更糟的是,很多框架(如 MyBatis)默认不开启 JDBC 的 setQueryTimeout(),等于把所有超时权都交给了数据库和网络。
- 给事务加超时:
@Transactional(timeout = 5) - HikariCP 开启泄露检测:
leakDetectionThreshold=30000(30 秒) - Druid 启用移除泄漏连接:
removeAbandonedOnBorrow=true+removeAbandonedTimeout=60
真正难排查的,是那些 COMMIT 慢得不明显(比如平均 300ms)、但又高频发生的场景——它不会立刻打爆池子,却会让连接池长期处于 high-active 状态,稍有流量波动就雪崩。这时候光看 SHOW PROCESSLIST 是不够的,得结合应用侧的 commit 耗时埋点和数据库的 performance_schema.events_statements_history 对齐时间戳。


















