SAVEPOINT 是事务内标记点而非独立事务,仅对当前未提交事务有效,回滚不释放锁且自增ID不回退,命名需唯一并带上下文,推荐用短事务替代。

SAVEPOINT 本质是事务内的标记点,不是独立事务
MySQL 的 SAVEPOINT 不会开启新事务,它只是在当前事务中打一个可回退的锚点。这意味着:事务整体仍受 AUTOCOMMIT 控制;所有 SAVEPOINT 都只对当前连接、当前未提交事务有效;一旦执行 COMMIT 或显式 ROLLBACK,所有保存点自动失效。
常见错误是以为 SAVEPOINT 能跨语句“隔离”操作——比如插入 A 表后设点,再删 B 表,然后只回滚删 B 的动作。实际上,如果中间有 DML 影响了同一行(如 UPDATE 同一行两次),ROLLBACK TO sp 会把整行状态倒回到保存点时的样子,不是“撤销某条语句”。
-
SAVEPOINT名字区分大小写,且不能与变量名或表名冲突(例如SAVEPOINT user在有user表时会报错) - 重复定义同名保存点会覆盖前一个,不会报错
- 嵌套事务不被 MySQL 原生支持(5.7/8.0 均无真正嵌套事务),
SAVEPOINT是唯一模拟局部回滚的机制
ROLLBACK TO sp 只回滚之后的变更,不释放锁
ROLLBACK TO SAVEPOINT sp 会撤销从该点之后的所有 DML 对数据的修改(INSERT/UPDATE/DELETE),但**不会释放这些语句持有的行锁或间隙锁**。这是最容易被忽略的性能隐患:事务仍在运行,锁还挂着,其他会话可能被阻塞。
典型场景:在长事务中频繁设点并回滚,表面看逻辑分段了,实际锁持续累积,最终导致锁等待超时(ERROR 1205 (HY000): Deadlock found when trying to get lock)或严重延迟。
- 执行
ROLLBACK TO sp后,必须继续COMMIT或ROLLBACK才能真正结束事务、释放锁 - 用
SELECT * FROM performance_schema.data_locks可查当前事务持有的锁,验证是否残留 - 若只需丢弃部分逻辑,更安全的做法是提前拆分为多个短事务,而非依赖
SAVEPOINT
实战中 SAVEPOINT 命名要带上下文和时间戳
多人协作或复杂存储过程里,硬编码 SAVEPOINT sp1 极易冲突或难以定位问题。命名应体现用途+位置,比如业务模块缩写+步骤序号+微秒时间戳(避免并发重复)。
SAVEPOINT order_insert_1724568901234; INSERT INTO orders (...) VALUES (...); SAVEPOINT order_payment_1724568901567; INSERT INTO payments (...) VALUES (...); -- 若支付失败,只回滚支付部分 ROLLBACK TO order_payment_1724568901567;
注意:SAVEPOINT 名不能含空格、点号或特殊符号,建议只用字母、数字、下划线。
- MySQL 8.0.23+ 支持
RELEASE SAVEPOINT sp显式删除保存点,减少内存占用 - 在存储过程中使用
DECLARE EXIT HANDLER捕获异常时,ROLLBACK TO必须在 handler 内显式调用,不会自动触发 - MyISAM 引擎不支持
SAVEPOINT,仅 InnoDB 和 NDB 支持
ROLLBACK TO 后无法再 COMMIT 当前事务的剩余部分
这是最常踩的坑:执行 ROLLBACK TO sp 后,误以为可以继续执行后续 SQL 并 COMMIT。实际上,MySQL 允许这么做,但**已回滚的变更不可逆,且事务状态仍是“活跃但部分撤销”的模糊态**——尤其当回滚涉及外键约束、触发器或自增主键时,行为可能不符合预期。
例如:先 INSERT 一条记录(自增 ID=100),设点,再 INSERT 一条(ID=101),然后 ROLLBACK TO,ID=101 被撤销,但自增计数器不会回退。下次插入仍是 ID=102,造成 ID 空洞。更严重的是,如果第二条 INSERT 触发了某个 ON INSERT 触发器,该触发器的副作用(如写日志表)不会被回滚。
- 推荐模式:每个逻辑单元用独立事务,而不是靠
SAVEPOINT拼接 - 若必须用
SAVEPOINT,回滚后应尽快COMMIT或ROLLBACK整个事务,不要继续混用新 DML - 测试时务必检查外键引用、触发器执行、自增字段、临时表等边缘行为
锁没释放、自增不回退、触发器副作用残留——这些都不是文档里高亮写的,但线上出问题时全在这些地方。










