mysql存储过程内savepoint与外层完全隔离,仅在本作用域生效;回滚不释放锁,命名需带业务上下文,ddl会隐式提交致保存点失效。

不能跨作用域引用外层 SAVEPOINT,存储过程内部的 SAVEPOINT 与调用者完全隔离——这是最常踩的坑。
存储过程内设点必须自建,不能复用外层同名保存点
MySQL 的存储过程(或函数、触发器)执行时会自动创建新的 savepoint 作用域。即使你在外部事务中设了 sp1,进到存储过程里再执行 SAVEPOINT sp1,它也和外面那个不是同一个;更关键的是:外部设的 sp1 在过程内根本不可见。
- 过程内所有
SAVEPOINT、ROLLBACK TO SAVEPOINT、RELEASE SAVEPOINT都只在本作用域生效 - 过程退出时,它自己创建的所有保存点自动释放,不影响外层事务状态
- 如果想“传递”回滚意图,只能靠返回值或 OUT 参数通知调用方,由外层决定是否回滚
ROLLBACK TO SAVEPOINT 在过程里能用,但锁不释放
过程里执行 ROLLBACK TO SAVEPOINT sp_check 确实能撤销该点之后的 DML(如 INSERT、UPDATE),但 InnoDB 不会释放这些操作持有的行锁——尤其是新插入行的锁,要等到整个事务 COMMIT 或最终 ROLLBACK 才真正释放。
- 这意味着:过程回滚后,其他事务仍可能被阻塞(比如你插入又回滚了一条记录,另一事务想更新同一行,会卡住)
-
SELECT ... FOR UPDATE拿的锁同样保留,过程结束也不释放 - 验证是否真回退?别信语句返回的 “Query OK”,直接
SELECT查表;查锁状态用information_schema.INNODB_TRX和INNODB_LOCK_WAITS
命名必须带上下文,避免同名覆盖静默失效
同一个存储过程中多次执行 SAVEPOINT sp1,后一次会静默覆盖前一次——不报错、不警告,但旧的 undo log 位置和 binlog offset 已丢失。再 ROLLBACK TO SAVEPOINT sp1 只能回到最后一次设点的位置。
- 建议用业务含义命名,例如
sp_before_validate_user、sp_after_insert_order_header - 不要依赖“默认命名”或简单编号,尤其在循环或条件分支里设点时
- 设完点如确认不再需要,主动
RELEASE SAVEPOINT sp_xxx,避免长期占用内存(虽单个仅几十字节,但过程反复调用易累积)
DDL 或隐式提交会让过程内所有 SAVEPOINT 立即失效
哪怕在存储过程内部执行一条 ALTER TABLE 或 DROP TEMPORARY TABLE,MySQL 也会隐式触发 COMMIT,导致该事务中此前所有保存点(包括过程内外的)全部消失。
- 错误现象:
ERROR 1305 (42000): SAVEPOINT sp1 does not exist,往往就因为前面某句 DDL 或TRUNCATE搞的 - 过程里尽量避免 DDL;如必须,应在 DDL 前先
COMMIT当前事务,另起一个新事务处理后续逻辑 - 注意:存储过程参数为
OUT或INOUT时,其值变更不受ROLLBACK TO SAVEPOINT影响——变量修改是会话级的,不参与事务控制
真正难的不是语法,而是理解 SAVEPOINT 从不“隔离”锁、不“快照”数据、也不“嵌套”事务——它只是 undo log 的一个指针。过程里设点再回滚,看起来像局部成功,其实锁还在、binlog 位点已跳、其他事务感知不到你的“局部”。用之前,先想清楚:你是真需要回滚,还是该拆成多个小事务 + 应用层补偿?











