MySQL事务需手动控制时应关闭AUTOCOMMIT,用START TRANSACTION显式开启,COMMIT/ROLLBACK后连接仍保持原AUTOCOMMIT状态;DDL会隐式提交,不可回滚,线上改表须用pt-online-schema-change等工具。

MySQL 事务开启后不自动提交,AUTOCOMMIT 是关键开关
InnoDB 默认每条 SQL 都是独立事务,执行完立刻生效——这不是你“没写 COMMIT”的问题,而是 AUTOCOMMIT 开着。关掉它,才能手动控制事务边界。
实操建议:
- 用
SELECT @@autocommit;查当前状态(1 = 开,0 = 关) - 临时关闭:执行
SET AUTOCOMMIT = 0;,之后所有 DML(INSERT/UPDATE/DELETE)都进入未提交状态 - 全局关闭需改配置文件
my.cnf,加一行autocommit=0,但生产环境慎用——多数 ORM 和连接池依赖默认行为 - 注意:
SET AUTOCOMMIT = 0只对当前会话生效,断连即恢复默认
BEGIN、START TRANSACTION 和空事务块的区别
它们仨作用完全一样,都是显式开启一个新事务,但语义和兼容性有细微差别。
实操建议:
- 优先用
START TRANSACTION,SQL 标准语法,MySQL 官方推荐,未来兼容性更好 -
BEGIN是别名,但某些老版本或存储过程里可能被误解析为语句块开始(尤其嵌套时),不推荐在复杂逻辑中混用 - 不要写空事务块:比如只
START TRANSACTION;然后就查数据——InnoDB 不会在只读查询时真正分配事务 ID,但会占用连接状态,容易引发锁等待误判 - 一旦执行了
START TRANSACTION,后续第一个修改语句才真正激活事务,之前SELECT仍是快照读,不加锁
COMMIT 和 ROLLBACK 后事务状态与连接复用问题
事务结束不是“清空一切”,而是释放锁、写 redo log、更新事务视图——连接本身仍处于活跃状态,且 AUTOCOMMIT 状态不变。
实操建议:
-
COMMIT成功后,事务彻底结束,但连接仍保持AUTOCOMMIT = 0(如果之前关了),下一条 DML 会自动开启新事务 -
ROLLBACK会丢弃所有未提交变更,但不会重置AUTOCOMMIT值,也不会释放表级元数据锁(MDL),这点常被忽略 - 长事务(比如交互式客户端手动开事务后挂起)会持续持有行锁和间隙锁,阻塞其他会话,监控看
INFORMATION_SCHEMA.INNODB_TRX表的TRX_STARTED和TRX_STATE - 应用层务必保证每个
START TRANSACTION都有对应COMMIT或ROLLBACK,否则连接可能卡在 “Sleep” 状态却挂着锁
事务中执行 DDL(如 ALTER TABLE)会隐式提交
InnoDB 对 DDL 的处理很暴力:只要碰到 CREATE、DROP、ALTER、RENAME 这类语句,不管你在不在事务里,都会立刻触发隐式 COMMIT,之前的修改全部落库,且无法回滚。
实操建议:
- 这是硬限制,不是 bug。哪怕你写了
START TRANSACTION; ALTER TABLE t ADD c INT; ROLLBACK;,ALTER执行完事务已结束,ROLLBACK实际无效 - 想安全改表?先确认是否在事务中:
SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();,非空则说明当前有活跃事务,应避免 DDL - MySQL 8.0+ 支持部分 DDL 的原子性(如
ADD COLUMN),但依然不支持在显式事务中执行——它自己就是个独立事务 - 线上变更务必用
pt-online-schema-change或gh-ost,它们绕过 DDL 隐式提交机制,靠拷贝+触发器实现无锁改表
事务不是开关,是状态机;InnoDB 的事务行为藏在 AUTOCOMMIT、隐式提交、锁生命周期这些细节里。漏掉任意一环,都可能让“以为回滚了”的数据实际已落盘,或者让“只读查询”意外阻塞写入。


















