事务回滚在存储过程中不生效,绝大多数情况不是ROLLBACK语法错,而是事务上下文已断或未建立:autocommit=1导致每条语句自动提交;DDL语句触发隐式COMMIT;存储过程自行BEGIN/ROLLBACK破坏外层事务;MyISAM引擎不支持事务;未声明EXIT HANDLER导致异常后事务悬空。

事务回滚在存储过程中不生效,绝大多数情况不是 ROLLBACK 语法错了,而是它根本没东西可滚——事务上下文早就断了或压根没建立。
autocommit=1 导致每条语句自动提交
MySQL 默认开启自动提交,START TRANSACTION 之后的语句确实会进事务,但一旦过程里没显式开启事务,或者调用方没先 BEGIN,那过程内所有 INSERT/UPDATE 都是独立事务,执行完立刻落盘。
- 检查当前会话:执行
SELECT @@autocommit;,结果为1就确认处于自动提交模式 - 存储过程内部不能靠
SET autocommit = 0改变调用方会话状态,它只影响自身作用域(且不可靠) - 正确做法:由调用方显式执行
START TRANSACTION,再调用存储过程
DDL 语句触发隐式 COMMIT
ALTER TABLE、DROP INDEX、CREATE PROCEDURE 这类 DDL 操作,在执行瞬间就会把当前事务隐式提交,后续 ROLLBACK 对此前 DML 无效。
- 现象:过程里先
INSERT,再ALTER TABLE,最后ROLLBACK→ 插入数据仍在 - DDL 不支持回滚,这是数据库引擎层行为,与存储过程无关
- 规避方式:DML 和 DDL 必须拆到不同会话或不同事务块中执行
存储过程自行 BEGIN / ROLLBACK 破坏外层事务一致性
SQL Server 和 MySQL 都不支持真正嵌套事务。BEGIN TRANSACTION 在过程内只会增加 @@TRANCOUNT,但任意一层 ROLLBACK 都会清空全部计数并终止整个事务,导致调用方无法控制回滚边界。
- 典型报错:
Msg 266, Level 16(事务计数不匹配) - 子过程不该调用
BEGIN/COMMIT/ROLLBACK TRANSACTION,应通过RETURN或输出参数通知错误 - 如需局部回滚,必须用
SAVE TRANSACTION sp_name+ROLLBACK TO sp_name,且保存点名不能是变量
引擎不支持事务或未启用错误处理器
如果表用的是 MyISAM 引擎,START TRANSACTION 和 ROLLBACK 完全被忽略,且不报错;MySQL 存储过程中异常不会自动中断执行,若没配 DECLARE EXIT HANDLER,过程会继续跑完,事务状态悬空。
- 查引擎:执行
SHOW CREATE TABLE your_table;,确认含ENGINE=InnoDB - 必须显式声明处理器:
DECLARE EXIT HANDLER FOR SQLEXCEPTION,并在其中写ROLLBACK - Handler 里别再执行可能失败的操作(比如写日志表),否则原始错误会被掩盖
最常被忽略的一点:ROLLBACK 是否真执行了,不能看有没有报错,得查 INFORMATION_SCHEMA.INNODB_TRX 表里对应连接的记录是否消失,否则你只是“以为”回滚了。

















