事务是所有数据库系统中的基本概念。它的核心作用,是把多个步骤组合成一个要么全部完成、要么完全不发生的操作。步骤之间的中间状态不会暴露给其他并发事务;如果某个故障使事务无法完成,其中任何一步都不会对数据库产生最终影响。
假设一家银行的数据库既保存各客户账户的余额,也保存各分行的存款总余额。现在要记录 Alice 向 Bob 支付 100.00 美元。把业务大幅简化后,SQL 可以写成下面这样:
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');
这里不必关心各条命令的细节。重点是:完成这样一个看似简单的操作,也需要分别执行多次更新。银行需要确保这些更新要么全部发生,要么全都不发生。系统故障不能导致 Bob 收到 100.00 美元,而 Alice 的账户没有扣款;反过来,Alice 被扣款但 Bob 没有收到钱,同样无法接受。
我们需要一种保证:如果操作进行到一半出了问题,此前已经执行的步骤也不会生效。把这些更新放进一个事务就提供了这种保证。这称为事务的原子性:从其他事务的视角看,该事务要么完整发生,要么完全没有发生。
我们还需要另一种保证:事务一旦完成并获得数据库系统确认,就已经永久记录下来,即使紧接着发生崩溃也不会丢失。比如,在记录 Bob 提取现金时,不能出现他刚走出银行,账户扣款就因为崩溃而消失的情况。
事务型数据库会在报告事务完成之前,把事务的所有更新记录到永久存储,也就是磁盘上。
事务型数据库的另一个重要性质,与原子更新密切相关:多个事务并发运行时,任何一个都不应看到其他事务尚未完成的修改。假设某个事务正在汇总各分行余额,它不能计入 Alice 所属分行的扣款,却漏掉 Bob 所属分行的入账;反过来也一样。
所以,“全部发生或完全不发生”不仅适用于事务对数据库的永久影响,也适用于执行期间的可见性。一个尚未完成的事务此前进行的更新,在事务结束前对其他事务不可见;事务完成时,所有更新会作为一个整体变得可见。
用 BEGIN 与 COMMIT 包围更新
在 PostgreSQL 中,把事务中的 SQL 命令放在 BEGIN 与 COMMIT 之间即可建立事务。上面的银行操作实际会采用这样的结构:
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
-- etc etc
COMMIT;
如果事务进行到一半时,我们决定不提交——例如刚发现 Alice 的余额变成了负数——可以执行 ROLLBACK 来代替 COMMIT,此前进行的所有更新就会被取消。
PostgreSQL 实际上会把每条 SQL 语句都视为在事务中执行。没有显式发出 BEGIN 时,每条单独的语句都会被隐式的 BEGIN 和成功时的 COMMIT 包围。由显式 BEGIN 与 COMMIT 包围的一组语句,有时称为事务块。
有些客户端库会自动发出 BEGIN 和 COMMIT,因此即使你没有明确要求,也可能已经得到事务块的行为。应检查实际使用的客户端接口文档,了解自动提交及事务边界。
用保存点撤销事务中的一部分
保存点允许更细粒度地控制事务中的语句:可以选择丢弃事务的一部分,同时提交其余部分。用 SAVEPOINT 定义保存点之后,需要时可以用 ROLLBACK TO 回滚到该保存点。建立保存点与回滚到它之间的数据库修改都会被丢弃,而保存点之前的修改会保留。
回滚到保存点之后,该保存点仍然存在,因此可以多次回滚到它。反过来,如果确定不再需要回滚到某个保存点,可以释放它,让系统回收部分资源。无论是释放保存点,还是回滚到保存点,都会自动释放在它之后定义的所有保存点。
这些操作都发生在事务块内部,因此其他数据库会话还看不到这些修改。只有当事务块最终提交时,提交的操作才会作为一个整体对其他会话可见;已经回滚的操作则从未变得可见。
继续看银行例子。我们从 Alice 的账户扣除 100.00 美元,并给 Bob 的账户入账,后来才发现收款人应该是 Wally。可以用保存点实现:
BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Wally';
COMMIT;
这个例子当然经过了大幅简化,但已经说明保存点可以在事务块内提供许多控制能力。此外,当系统因为错误把事务块置于已中止状态时,除了把整个事务回滚后重新开始,ROLLBACK TO 是恢复该事务块控制的方式。前提是错误之前已经建立了可回滚到的保存点。
阅读示例时的边界
上述三个 SQL 块完整保留官方教程的语句、注释和顺序;第二个块中的 -- etc etc 是省略其他更新的标记,不是完整银行业务实现。这些片段依赖已经存在的 accounts、branches 表与示例账户,没有在本文制作环境中执行。真实应用还应结合隔离级别、并发控制和客户端事务管理规则设计完整流程;这段基础教程没有覆盖这些主题。PostgreSQL 事务隔离文档
来源与许可
来源:PostgreSQL 18 官方教程,第 3.4 节 Transactions,作者为 PostgreSQL Global Development Group 及文档贡献者。本稿完整汉化该节正文,调整分节与中文术语,并增加示例边界及未运行说明;代码保持逐字一致。整理日期:2026-10-03。
官方法律声明 明确允许使用、复制、修改和分发软件及其文档。以下版权和许可通知完整保留:
PostgreSQL Database Management System (also known as Postgres, formerly known as Postgres95)
Portions Copyright © 1996-2026, PostgreSQL Global Development Group
Portions Copyright © 1994, The Regents of the University of California
Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN “AS-IS” BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.











暂无评论内容