sql存储过程内禁止使用start transaction和commit,必须由调用方统一控制事务边界;mysql中start transaction会隐式提交前一事务,sql server中rollback会清零@@trancount导致外层事务失效,savepoint仅回滚数据不释放锁。

SQL存储过程里根本不存在嵌套事务——所谓“嵌套”,只是让@@TRANCOUNT计数失真、触发隐式提交或导致回滚报错。
MySQL中START TRANSACTION会立刻提交前一个事务
这是最常踩的坑:应用层已开启事务,调用含START TRANSACTION的存储过程,刚进过程那条INSERT就落库了,再也ROLLBACK不回去。
- 错误现象:
ERROR 1305 (42000): SAVEPOINT does not exist,或“回滚后数据还在” - 根本原因:
START TRANSACTION不是压栈,是中断+重置;MySQL单连接只允许一个活跃事务 - 正确做法:存储过程体内绝不能写
START TRANSACTION和COMMIT,由调用方统一控制事务边界 - 验证方式:执行前确认
AUTOCOMMIT = 0,否则SAVEPOINT无效
SQL Server中重复BEGIN TRANSACTION只增@@TRANCOUNT,任意ROLLBACK清零全部
BEGIN TRANSACTION只是给@@TRANCOUNT加1,而ROLLBACK TRANSACTION(无论带不带名字)都会把@@TRANCOUNT直接归零——外层事务瞬间失效。
- 典型报错:
Msg 266, Level 16, State 2: Transaction count after EXECUTE indicates a mismatch - 子过程自己写
BEGIN TRANSACTION+ROLLBACK,会导致外层COMMIT时发现@@TRANCOUNT = 0而报错 - 唯一可控方案:用
SAVE TRANSACTION savepoint_name设锚点,再用ROLLBACK TRANSACTION savepoint_name局部回滚 - 硬性限制:保存点名必须是字面量(如
savepoint_orderdetails),不能是变量;且执行前@@TRANCOUNT > 0,否则报Msg 628
回滚到SAVEPOINT后锁不会自动释放
很多人以为ROLLBACK TO SAVEPOINT或ROLLBACK TRANSACTION savepoint_name会“清理干净”,其实它只撤销数据修改,不释放该点之后语句持有的行锁、间隙锁。
- 这些锁会一直卡着,直到整个事务
COMMIT或最终ROLLBACK - 高并发下极易阻塞其他查询,甚至触发
ERROR 1205 (HY000): Deadlock found when trying to get lock - 验证残留锁:查
performance_schema.data_locks(MySQL)或sys.dm_tran_locks(SQL Server) - 没有银弹:局部回滚 ≠ 局部释放资源,锁生命周期始终绑定到最外层事务
真正难处理的不是语法怎么写,而是锁生命周期和事务边界的错位——你回滚了数据,但没回滚掉锁,别人就卡住了。这点在读已提交或可重复读隔离级别下尤其隐蔽。











