MySQL事务不会自动回滚,仅在显式开启且未提交时才可回滚;默认autocommit=1使每条SQL独立生效;需手动BEGIN/START TRANSACTION并配合ROLLBACK或异常处理器(如DECLARE EXIT HANDLER FOR SQLEXCEPTION)才能确保出错回滚。

事务自动回滚只发生在显式开启且未提交的场景
MySQL 默认是自动提交(autocommit=1)模式,每条 SQL 都是一个独立事务,执行完立刻生效,根本不存在“回滚”一说。只有当你手动执行 START TRANSACTION 或 BEGIN,或者把 autocommit 设为 0,后续语句才会被纳入同一个事务上下文——这时才可能触发回滚。
常见误解是“只要报错就自动回滚”,其实不然:比如在存储过程中执行一条 INSERT 报了 ERROR 1062 (23000): Duplicate entry,若没做异常捕获,事务仍处于活跃状态,后续 COMMIT 会把前面成功的语句一起提交;必须靠 ROLLBACK 或异常处理器干预。
存储过程里用 DECLARE HANDLER 捕获错误再回滚
在 CREATE PROCEDURE 中处理异常,核心是声明错误处理器,而不是依赖 MySQL 自动行为。需要明确指定错误类型(SQLSTATE 或 MySQL 错误码),并搭配 ROLLBACK 使用。
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END;
注意点:
-
EXIT表示触发后退出当前复合语句块,适合放在事务体开头 -
SQLEXCEPTION覆盖大部分错误,但不包括警告(warning)或NOT FOUND;如需捕获主键冲突,可写DECLARE ... FOR 1062 -
RESIGNAL重新抛出原错误,避免静默失败;若去掉,调用方收不到错误信息 - 务必确保
START TRANSACTION在HANDLER声明之后、业务逻辑之前,否则 handler 不生效
应用层发起的事务必须由代码控制回滚时机
PHP/Python/Java 等客户端通过连接执行 BEGIN 后,MySQL 完全不感知你的业务逻辑是否成功。它只认你发来的 COMMIT 或 ROLLBACK 指令。
典型疏漏:
- try-catch 里只打印日志,忘了调用
connection.rollback() - 多个嵌套函数中,异常被中间层吞掉,最终既没 commit 也没 rollback,连接挂着一个未结束事务
- 超时或网络中断导致请求“消失”,MySQL 端事务长时间处于
ACTIVE状态,占用锁和 undo log
建议做法:用 try/finally 保证无论是否异常都显式结束事务;或使用支持事务传播的框架(如 Spring 的 @Transactional),但要清楚其默认 rollback 规则(仅对 unchecked exception 生效)。
隐式提交语句会让事务提前结束,无法回滚
有些 SQL 语句会强制触发隐式提交(implicit commit),导致当前事务立即结束,后续语句不在同一事务中。这类语句一旦执行,前面的修改就固化了,ROLLBACK 无效。
高频踩坑命令包括:
- DDL 类:
CREATE TABLE、DROP DATABASE、ALTER TABLE - 管理类:
ANALYZE TABLE、OPTIMIZE TABLE、FLUSH LOGS - 事务控制类:
START TRANSACTION本身(如果已有活跃事务,会先提交再开新事务)
验证方式:执行前查 SELECT @@in_transaction;,执行后为 0 就说明已被隐式提交。这类操作不适合塞进业务事务块里,应单独处理。


















