事务内部分回滚
保存点是事务内部的一个标记,出错时可以只回滚到这个标记,而不是把整个事务作废。批量处理场景里它特别有用:一批数据里个别行有问题,回滚到保存点后跳过继续,前面已经成功的部分不受影响。两种数据库在这块还有一个更底层的差别: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;行为要点
| 动作 | 效果 | 注意 |
|---|---|---|
| 建立标记 | 记录当前事务状态 | 可建立多个,按需要划分阶段 |
| 回滚到标记 | 撤销该标记之后的所有修改 | 标记之前的修改仍然保留 |
| 释放标记 | 删除标记,不改动数据 | 漏写也不影响提交 |
| 同名重建 | 旧标记被替换 | 循环里命名要清晰,避免误判 |
| 提交 | 标记之前的修改一起生效 | 别以为回滚过就整段撤销 |
最常见的误解是「回滚到保存点等于撤销这段事务」:回滚只影响标记之后的部分,标记之前的插入仍然会在提交时生效。核对结果时要按标记划分清楚哪部分该留、哪部分该没。
结构变更类语句会隐式提交当前事务,标记随之失效。批处理脚本里如果夹带了建表或加列操作,保存点机制会静默失效,脚本逻辑需要重新设计。