47 保存点
保存点是事务中的一个命名位置。你可以使用 ROLLBACK TO SAVEPOINT name 撤销该位置之后完成的工作,同时保留之前的全部工作。保存点是 PostgreSQL 实现类似嵌套事务语义的方式——当你想尝试一个有风险的步骤、从错误中恢复并继续执行,或者应用程序代码模拟嵌套事务时,它们很有用。
本章要点
- 使用
SAVEPOINT name设置保存点 - 回滚到保存点
- 释放保存点
- 从事务中的错误恢复
- 保存点与
BEGIN/COMMIT的关系
示例
SAVEPOINT name 标记一个位置。ROLLBACK TO SAVEPOINT name 撤销保存点之后直到现在的所有操作,但事务会继续——你可以继续进行工作,最终执行 COMMIT。RELEASE SAVEPOINT name 会忘记该保存点(之后不能再回滚到它),但不会撤销任何工作。
应用框架通过保存点模拟“嵌套事务”:当应用代码已经处于事务中又调用 begin_transaction 时,驱动会设置保存点,而不是启动新的顶层事务。内部代码块中的“回滚”会回到该保存点。
错误恢复是一个重要场景。在事务中,任何错误都会使事务进入“中止”状态,此后每条语句都会失败,直到执行 ROLLBACK。使用保存点时,你可以执行 ROLLBACK TO SAVEPOINT before_risky 并继续——事务会恢复到可用状态。
运行示例
运行方式:
常见陷阱
- 保存点有开销。包含数千个保存点的事务(常见的 ORM 模式)可能开销很大。
- 执行
ROLLBACK TO SAVEPOINT s后,保存点s本身仍然存在——你可以再次回滚到它。要移除它,请使用RELEASE。 RELEASE SAVEPOINT s不会“提交”工作——只有包含它的事务执行COMMIT才算提交。RELEASE只是移除标记。- 保存点只存在于显式事务中。在显式事务之外执行
SAVEPOINT s;会报错。
ORM 经常用保存点实现“事务性”测试:每个测试都在一个保存点中运行,测试结束时回滚到该保存点,从而不改变数据库。可以查阅 pytest-postgresql、RSpec 的事务性测试夹具等。
添加第二个保存点,插入更多行,然后 ROLLBACK TO 第一个保存点,看看保存点之后的所有工作是否都被撤销。尝试错误恢复模式:BEGIN; INSERT ... SAVEPOINT s; INSERT (bad row); ROLLBACK TO SAVEPOINT s; INSERT (good row); COMMIT;。
下载
SQL 文件:savepoints.sql · 全部示例文件 · 准备工作