MySQL 中 SA VEPOINT 之所以没有生效,根本原因通常不在 SA VEPOINT 语句本身,而是当前会话实际上并未真正进入事务。例如在 autocommit=1 的场景下,SA VEPOINT 看上去像是创建成功了,但本质上只是“伪创建”;想让保存点真正可用,必须同时满足 @@autocommit=0 和 @@in_transaction=1,并且还需要显式执行 BEGIN 或 START TRANSACTION。除此之外,还有几个常见误区也必须注意:DDL 会触发隐式提交,并把所有 SA VEPOINT 一并清除;同名 SA VEPOINT 不会报错,而是会被直接静默覆盖;SA VEPOINT 仅在当前数据库连接内有效;一旦事务执行 COMMIT 或完整 ROLLBACK,之前创建的保存点也会自动失效。

SA VEPOINT根本没生效,事务没有真正开启
最常见的原因就是当前会话并不处于事务中:当 autocommit=1 时,执行 SA VEPOINT sp1 虽然表面上返回 Query OK,但实际上只是“伪创建”——下一条语句开始后就已经进入新的事务上下文,之前的保存点自然随之失效。
要判断是否真正进入事务,必须同时满足以下两个条件:
SELECT @@autocommit返回0SELECT @@in_transaction返回1
需要特别注意的是,仅执行 SET autocommit = 0 还不够,必须显式执行 BEGIN 或 START TRANSACTION,才能真正建立有效的事务上下文。
DDL语句触发隐式提交,所有保存点被清空
只要在事务中执行过 ALTER TABLE、DROP INDEX、CREATE TABLE 等 DDL 语句,MySQL 就会立刻隐式执行 COMMIT,当前事务随即结束,之前创建的所有 SA VEPOINT 也会全部消失。
实际表现非常直观:前面刚创建好 SA VEPOINT sp_after_insert,后面如果执行 ALTER TABLE t ADD COLUMN x INT,那么再次执行 ROLLBACK TO SA VEPOINT sp_after_insert 时,就一定会报错 ERROR 1305 (42000): SA VEPOINT sp_after_insert does not exist。
这并不是 MySQL 的 bug,而是其事务机制中的既定行为。因此在执行 DDL 之前必须提前评估:是否真的需要修改表结构?能不能先完成结构调整?或者将 DDL 拆分到事务外单独执行?
保存点名称被覆盖,或目标保存点本身不存在
同名 SA VEPOINT 会直接静默覆盖前一个保存点,不会报错,也不会给出警告。比如在循环中写死 SA VEPOINT loop_step,每次执行都会覆盖上一次的同名保存点,因此 ROLLBACK TO SA VEPOINT loop_step 永远只会回到最后一次设点的位置,而不是你原本预期的某一轮迭代起点。
命名时建议使用带业务上下文的短标识,例如:sp_before_order_insert、sp_batch_123;名称长度不能超过 64 个字符,超长部分会被截断;同时要注意大小写敏感(sp1 和 SP1 属于不同保存点)。
另外,SA VEPOINT 只在当前连接内有效,无法跨连接使用。如果在其他连接里执行 ROLLBACK TO SA VEPOINT,也一定会失败,通常同样返回 ERROR 1305。
事务已经结束,保存点会自动销毁
当执行 COMMIT 或完整 ROLLBACK(不带 TO SA VEPOINT)后,整个事务生命周期就已经结束,所有 SA VEPOINT 都会被自动清除。此时再执行 ROLLBACK TO SA VEPOINT sp1,同样会报出 ERROR 1305。
还要注意一点:ROLLBACK TO SA VEPOINT sp1 本身并不会结束事务,它只是撤销该保存点之后的 DML 操作;但如果你在这之后又执行了一次不带参数的 ROLLBACK,那么事务就会被真正终止,后续任何保存点相关操作都会失效。
另一个常被忽略的问题是锁状态:回滚到保存点之后,数据看起来虽然恢复了,但相关行锁未必立即释放,尤其是新插入记录上的意向锁。要确认真实的锁持有情况,通常还需要结合 information_schema.INNODB_TRX 进行检查。
