autocommit=false 会导致连接被事务长期占用不释放,而非连接数增长;因事务未显式提交或回滚时,close() 仅标记可复用,实际仍绑定在未结束事务上,连接池无法回收。

autocommit=false 直接导致连接被事务锁死,不是连接数“增长”,而是连接被长期占用不释放
autocommit=false 为什么会让连接卡住不归还池子
MySQL 默认 autocommit=true,每条 SQL 执行完自动提交,连接能立刻回到空闲状态;设为 false 后,后续所有 SQL 都进入同一个事务,直到显式执行 COMMIT 或 ROLLBACK 才结束。如果代码里漏了这两句(尤其在异常分支),事务就一直挂着,连接也就一直被占着——连接池无法回收它,close() 调用只是把连接“标记为可复用”,但实际仍绑定在未结束的事务上。
- 常见错误模式:
try块里执行 SQL,catch里只记录日志没rollback(),finally里调conn.close()—— 这个close()不会中断事务,连接仍处于ACTIVE状态 - HikariCP 的
close()是轻量级操作,真正释放依赖事务结束;Druid 也类似,不会强制中断事务 - 查证方式:运行
SELECT * FROM information_schema.INNODB_TRX WHERE trx_state = 'RUNNING',看是否有trx_started时间远早于当前时间的事务
SHOW PROCESSLIST 里看到的 Sleep 连接其实不是真 Sleep
当 autocommit=false 且事务未结束时,连接在 PROCESSLIST 中的 Command 可能显示为 Sleep,但 State 是 committing、updating 或空,Time 持续增长。这不是空闲连接,而是事务上下文未清理的“假休眠”。
- 关键区别:真 Sleep 是等待新指令,假 Sleep 是事务卡在中间态
- 执行
SHOW ENGINE INNODB STATUS\G,在TRANSACTIONS部分找mysql tables in use和locked tables数量异常高的条目 - 如果
Time列超过wait_timeout却没断开,基本可判定是事务阻塞而非网络空闲
autocommit=false 场景下必须配套的防护措施
手动控制事务不是不能用,但必须有兜底机制,否则等于给连接池埋雷。
- 所有
setAutoCommit(false)必须配对try-finally或try-with-resources+ 显式rollback()在catch和finally里 - 连接池配置要启用事务超时:HikariCP 设置
connection-timeout(获取连接超时)和transaction-isolation,但更关键的是业务层设置setQueryTimeout()防止单 SQL 卡死 - 禁止在事务内调用外部服务(HTTP/RPC)、循环处理大批量数据、或执行耗时计算——这些都会拉长事务生命周期
- 监控项要加一条:
innodb_trx.trx_started与当前时间差 > 30 秒的告警,比单纯看Threads_connected更早发现问题
真正危险的不是 autocommit=false 本身,而是把它当成普通开关用,忽略了事务边界必须由代码严格定义。一个没 rollback() 的 catch 块,比十个慢查询更能拖垮整个连接池。











