十二月 9, 2023
摘要:在本教程中,您将学习如何使用SAVEPOINT
、ROLLBACK TO SAVEPOINT
和RELEASE SAVEPOINT
语句,来处理 PostgreSQL 子事务。
目录
子事务简介
在专业的应用程序中,很难在不遇到任何错误的情况下编写相当长的事务。为了解决这个问题,用户可以使用一种叫做 SAVEPOINT 的东西。顾名思义,保存点是事务中的一个安全位置,如果出现严重错误,应用程序可以返回到该位置。
保存点是一个数据库特性,它允许您在事务中创建命名点,以后可以回滚到该命名点,同时保持事务的其余部分不变。当您想要处理事务中的错误或异常,并有选择地回滚到事务中的特定点,而不必撤消到目前为止所做的所有更改时,保存点非常有用。
怎么工作的
以下是在 PostgreSQL 中使用保存点的方法:
使用BEGIN
语句启动一个事务:
BEGIN;
这将开始一个新的事务。
在事务中,您可以使用SAVEPOINT
语句创建一个保存点,并指定一个名称:
SAVEPOINT my_savepoint;
在此示例中,在事务中创建了一个名为my_savepoint
的保存点。
在事务中执行一个或多个 SQL 操作,例如INSERT
、UPDATE
、DELETE
等。
在任何时候,如果需要回滚到保存点,可以使用ROLLBACK TO
语句:
ROLLBACK TO my_savepoint;
这将撤消在创建my_savepoint
保存点后所做的所有更改,从而有效地将事务还原到该点。
您还可以使用RELEASE
语句释放保存点:
RELEASE my_savepoint;
这将删除保存点,并允许事务从当前位置继续。
最后,当您准备好提交事务中所做的所有更改时,可以使用COMMIT
语句:
COMMIT;
这会将事务中所做的所有更改保存到数据库中。
示例
下面是一个完整的示例:
CREATE TABLE test0 AS SELECT 1 AS i;
-- Start a main transaction
BEGIN;
-- Perform some operations within the transaction
UPDATE test0 SET i = i + 1;
-- Start a subtransaction
SAVEPOINT s1;
-- Continue with more operations
UPDATE test0 SET i = i - 1000;
-- Check the content in the table
SELECT * FROM test0;
i
------
-998
(1 row)
-- Something went wrong, let's roll back to the savepoint
ROLLBACK TO SAVEPOINT s1;
-- Continue with other operations
UPDATE test0 SET i = i + 1;
-- Finally, when everything is fine, commit the transaction
COMMIT;
-- Check the content in the table again
SELECT * FROM test0;
i
---
3
(1 row)
谁在使用子事务?
如今,大多数流行的 ORM 和框架都原生支持嵌套事务。例如:
除了 SAVEPOINT 之外,还有其他方法可以创建子事务:
- PL/pgSQL 代码中的
BEGIN / EXCEPTION WHEN .. / END
块(官方文档没有很好地描述它;不过可通过该篇文章来进行了解:“PL/PgSQL 异常和子事务”)。 - PL/Python 代码中的 plpy.subtransaction()。
- 在 psql 中将
ON_ERROR_ROLLBACK
变量设置为 on。 - 使用 JDBC 驱动程序指定
autosave
连接参数。
可以假设许多使用 PL/pgSQL 或 PL/Python 函数的应用程序会使用子事务。运行构建在 PostgREST、Supabase、Hasura 的 API 上的系统,可能会有涉及BEGIN / EXCEPTION WHEN .. / END
块的 PL/pgSQL 函数(包括触发器函数);在这些情况下,那些系统会使用子事务。
总结
保存点允许您对事务进行更精细的控制,尤其是在复杂的场景中,您希望优雅地处理错误,并有选择地回滚到事务中的特定点的情况下。
事务中的保存点数量实际上是无限的。我们见到过客户在一次操作中拥有超过 250,000 个保存点。PostgreSQL 可以对此进行轻松的处理。
许多人会问,如果您在事务结束后尝试到达保存点会发生什么。答案是,一旦事务结束,保存点的生命周期就结束了。换句话说,在事务完成后,没有办法回到某个时间点。