必须关闭autocommit才能使begin生效,否则每条语句仍自动提交;连接池中未收尾的事务会隐式阻塞并发,需应用层兜底提交或回滚并定期巡检长事务。

关掉 autocommit 是必须的,否则 BEGIN 没有意义。
为什么 BEGIN 单独用不解决问题
很多人执行了 BEGIN,接着插入几条数据,再 COMMIT,却发现性能没提升——因为连接默认仍是 autocommit = 1。此时 BEGIN 只是开启一个事务,但只要下一条语句不是显式 COMMIT 或 ROLLBACK,MySQL 会在语句执行完自动提交,等效于每条 SQL 都是独立短事务。
验证方式:SELECT @@autocommit; 返回 1 就说明仍开着自动提交。
常见错误现象:
- 执行完
BEGIN和多条INSERT后,没发COMMIT就断开连接 → 事务回滚,数据全丢 - 应用层用连接池复用连接,上一个请求没
COMMIT,下一个请求一执行就报ERROR 1399 (XAE04): XAER_RMFAIL或锁等待超时 - 监控看到
Innodb_log_writes持续高位,和没加BEGIN一样
正确关闭自动提交并配合 BEGIN
必须在会话级显式关闭自动提交,之后 BEGIN 才真正起作用。注意:这不是全局设置,每个新连接都要重设。
实操步骤:
- 连接后第一件事:执行
SET autocommit = 0; - 再执行
BEGIN;(或START TRANSACTION;,二者等价) - 批量执行
INSERT/UPDATE,建议单批控制在 100–1000 行 - 最后统一
COMMIT;;异常路径必须ROLLBACK;
参数差异提示:
-
autocommit = 0后,所有 DML 都进入事务上下文,直到显式COMMIT或ROLLBACK - 若忘记
COMMIT,该连接后续所有操作都卡在同一个未结束事务里 - JDBC 中需调用
connection.setAutoCommit(false),Python 的conn.autocommit = False,不能只靠 SQL
容易被忽略的连接池陷阱
连接池(如 HikariCP、SQLAlchemy Pool)会复用物理连接。如果某次使用后没 COMMIT 或 ROLLBACK,下次从池中取出的连接可能带着残留事务状态。
典型表现:
- 第一次请求正常提交,第二次请求执行
SELECT ... FOR UPDATE立即阻塞 -
INFORMATION_SCHEMA.INNODB_TRX里能看到TRX_STATE = 'LOCK WAIT'且TRX_STARTED时间远早于当前请求 - 日志中反复出现
Lock wait timeout exceeded(错误码 1205)
规避方式:
- 应用层强制在请求结束前做“事务兜底”:无论成功失败,都确保
COMMIT或ROLLBACK - 数据库侧设
wait_timeout和interactive_timeout(如 60 秒),让空闲长事务自动断连 - 定期巡检:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 30;
真正耗时的从来不是 BEGIN 这个命令本身,而是没关 autocommit 导致它形同虚设;更隐蔽的问题是连接池里那些没人收尾的事务,它们不会报错,只会悄悄拖垮整个库的并发能力。











