SAVEPOINT未生效的主因是会话未真正开启事务:autocommit=1时SAVEPOINT为伪创建;必须同时满足@@autocommit=0且@@in_transaction=1,且需显式BEGIN/START TRANSACTION;DDL会隐式提交并清空所有SAVEPOINT;同名SAVEPOINT被静默覆盖;SAVEPOINT仅限当前连接有效;事务COMMIT或全量ROLLBACK后保存点自动销毁。
最常见原因是当前会话不在事务中:autocommit=1 时,SAVEPOINT sp1 看似执行成功(返回 Query OK),但其实是“伪创建”——下一条语句开始就已是新事务,保存点早已失效。
必须同时满足两个条件才算真正进事务:
SELECT @@autocommit 返回 0
SELECT @@in_transaction 返回 1
仅执行 SET autocommit = 0 不够,必须显式执行 BEGIN 或 START TRANSACTION 才能激活事务上下文。
只要事务中执行过 ALTER TABLE、DROP INDEX、CREATE TABLE 等 DDL,MySQL 就会立刻隐式执行 COMMIT,当前事务终结,所有已设的 SAVEPOINT 全部消失。
现象是:刚建完 SAVEPOINT sp_after_insert,接着执行 ALTER TABLE t ADD COLUMN x INT,再跑 ROLLBACK TO SAVEPOINT sp_after_insert,必然报错 ERROR 1305 (42000): SAVEPOINT sp_after_insert does not exist。
这通常不是异常,是 MySQL 的设计行为。DDL 前必须评估:是否真需要改结构?能否提前做完?或拆到事务外单独执行?
同名 SAVEPOINT 会静默覆盖前一个,不报错也不警告。比如循环里写死 SAVEPOINT loop_step,每次都会覆盖,ROLLBACK TO SAVEPOINT loop_step 永远回到最后一次设点位置,而非你预期的某次迭代起点。
命名建议用带上下文的短标识符,例如:sp_before_order_insert、sp_batch_123;长度不能超 64 字符,超长会被截断;大小写敏感(sp1 和 SP1 是不同点)。
另外,SAVEPOINT 只在当前连接有效,跨连接调用 ROLLBACK TO SAVEPOINT 一定失败,错误仍是 ERROR 1305。
COMMIT 或全量 ROLLBACK(不带 TO SAVEPOINT)之后,整个事务生命周期终结,所有 SAVEPOINT 自动清除。此时再执行 ROLLBACK TO SAVEPOINT sp1,照样报 ERROR 1305。
注意:ROLLBACK TO SAVEPOINT sp1 本身不会终止事务,它只撤销该点之后的 DML;但如果你误在它之后又执行了一次 ROLLBACK(无参数),那就真结束了事务,后续任何保存点操作都无效。
容易被忽略的是锁状态:回滚到保存点后数据看似还原了,但行锁可能还在(尤其是新插入行的意向锁),查 information_schema.INNODB_TRX 才能确认真实锁持有情况。