首页/数据/SQL 速查/SAVEPOINT 部分回滚
变更与事务52 条

SAVEPOINT 部分回滚

事务内设置保存点,出错时只回滚到某个点而不是整个事务:批量处理中“跳过坏行、保留好行”的常用手段。

SQL 速查数据更新于 2026-10-13

事务内部分回滚

保存点是事务内部的一个标记,出错时可以只回滚到这个标记,而不是把整个事务作废。批量处理场景里它特别有用:一批数据里个别行有问题,回滚到保存点后跳过继续,前面已经成功的部分不受影响。两种数据库在这块还有一个更底层的差别:PostgreSQL 里某条语句报错会让整个事务进入失败状态,后续语句一律被拒绝,想继续做别的事就必须先回滚到保存点。

基本信息

适用平台MySQL 8.0(InnoDB)/ PostgreSQL 12 及以上
建立标记事务内建立命名标记,可建立多个
回滚回滚到指定标记,该标记之后的修改被撤销
释放释放标记只删除标记本身,不影响已执行的数据
同名覆盖重复使用同名标记时,旧标记被替换

典型使用场景

  • 批量导入:单行数据有问题时回滚到标记后继续处理下一行
  • PostgreSQL 语句失败后继续:先回滚到保存点再决定下一步
  • 多步骤变更:某一步失败时保留前面已完成的步骤结果
  • 测试数据准备:反复回滚到同一状态做多次验证
  • 复杂事务的可观测性:用标记划分阶段,便于定位失败阶段

写法与结果

写法与结果
START TRANSACTION;
INSERT INTO t (id) VALUES (1);
SAVEPOINT sp1;                  -- 在这里打个标记
INSERT INTO t (id) VALUES (2);
ROLLBACK TO SAVEPOINT sp1;      -- 只撤销 id=2 的插入
RELEASE SAVEPOINT sp1;          -- 释放标记(可选,提交时自动清理)
COMMIT;                         -- 最终 id=1 生效,id=2 不存在

-- PostgreSQL:语句报错后事务进入失败状态,必须回滚到标记才能继续
BEGIN;
SAVEPOINT sp_load;
-- INSERT 语句在这里失败,事务被标记为失败
ROLLBACK TO SAVEPOINT sp_load;  -- 恢复到标记点,事务重新可用
INSERT INTO t (id) VALUES (3);  -- 可以继续执行
COMMIT;

行为要点

动作效果注意
建立标记记录当前事务状态可建立多个,按需要划分阶段
回滚到标记撤销该标记之后的所有修改标记之前的修改仍然保留
释放标记删除标记,不改动数据漏写也不影响提交
同名重建旧标记被替换循环里命名要清晰,避免误判
提交标记之前的修改一起生效别以为回滚过就整段撤销
最常见的误解是「回滚到保存点等于撤销这段事务」:回滚只影响标记之后的部分,标记之前的插入仍然会在提交时生效。核对结果时要按标记划分清楚哪部分该留、哪部分该没。
结构变更类语句会隐式提交当前事务,标记随之失效。批处理脚本里如果夹带了建表或加列操作,保存点机制会静默失效,脚本逻辑需要重新设计。

易错点

  • 以为回滚到标记就把整段事务撤销了:标记之前的修改仍会在提交时生效
  • 循环里使用同名标记,旧标记被覆盖,回滚到位点与预期不符
  • 在 PostgreSQL 里语句报错后不先回滚到标记就继续执行,所有后续语句都被拒绝
  • 批处理中夹带结构变更语句,触发隐式提交导致标记失效,脚本行为与设计不符
  • 保存点与嵌套事务混用:嵌套写法要求更严格,错误处理路径会复杂很多

数据来源与参考

  • MySQL 8.0 Reference Manual · SAVEPOINT
  • PostgreSQL Documentation · SAVEPOINT
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌