PostgreSQL 中 BEGIN、COMMIT 和 ROLLBACK 如何将多个更改组合为单个操作
PostgreSQL 中的事务通过 BEGIN、COMMIT 和 ROLLBACK 命令将多个 SQL 操作组合成原子性块。这确保了数据完整性:要么所有更改都应用,要么完全不应用。该机制基于 ACID 原则 - - 原子性、一致性、隔离性和持久性。
本文内容
简明答案
在 PostgreSQL 中,BEGIN、COMMIT 和 ROLLBACK 命令管理一个事务块,将多个操作组合成一个逻辑单元。执行 BEGIN 后,事务开始;后续更改不会立即提交。若执行 COMMIT,则所有更改永久保存。若执行 ROLLBACK,则自 BEGIN 以来的所有更改都将被撤销。这保证了原子性 - - 要么所有步骤成功,要么没有任何更改影响数据库。
PostgreSQL 中事务的介绍
事务是可靠数据库操作的基础。它们允许将多个 SQL 语句组合成一个单一的逻辑单元,以‘全部或无’的方式执行。这对于维护数据完整性至关重要,尤其是在错误可能导致不一致状态的系统中,例如银行转账。
PostgreSQL 根据 ACID 原则实现事务:原子性、一致性、隔离性和持久性。如文档所述,‘事务将多个步骤组合成一个不可分割的操作’ - - 如果发生故障,数据库不会受到任何中间更改的影响。
BEGIN、COMMIT 和 ROLLBACK 的语法
要显式管理事务,使用三个命令:BEGIN、COMMIT 和 ROLLBACK。BEGIN 开始一个事务块,之后的所有操作都在此事务内执行。COMMIT 将所有更改永久化。ROLLBACK 则取消自 BEGIN 以来所做的所有更改。
示例:从 Alice 转账 100 美元给 Bob 是作为单个事务完成的:
该示例假设存在一个 accounts 表,其中包含唯一名称和数值余额,并且已有 Alice 和 Bob 的记录。保存点示例还假设存在 Wally 的记录。请在受控演示数据库中运行这些语句。在应用程序代码中,应验证受影响的行数、资金充足性和所需约束;仅成功 COMMIT 并不能证明预期的业务转账已发生。
BEGIN;
UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Bob';
COMMIT;更改的原子性
原子性意味着事务要么完全成功,要么完全回滚。中间状态不会被保留。例如,如果从 Alice 账户扣除资金但尚未向 Bob 存入时发生故障,整个事务将被回滚,恢复 Alice 的余额。
WAL(预写日志)记录必须在相应修改的数据页写入之前到达持久存储。数据页可能在 COMMIT 之前就被写入;原子性并不意味着更改会一直保留在内存中直到提交。崩溃恢复依赖于日志,而可见性规则防止其他会话看到未提交的表更改。
默认自动事务
在显式事务块之外,PostgreSQL 会将每个语句作为一个隐式事务执行。客户端库可能会自动启动事务,或提供 autocommit 选项,因此在假设两个语句独立之前,请检查连接设置。
这种模式对简单操作很方便,但当多个命令必须一致执行时,必须使用显式的 BEGIN 和 COMMIT,以避免部分更改被应用。
错误处理:回滚更改
如果在事务过程中出现使事务无效的情况(例如余额为负),可以发出 ROLLBACK。自事务开始以来所做的所有更改都将被撤销。
例如,在从 Alice 扣款后,如果她的余额变为负数,可以回滚事务,恢复数据库的原始状态。
更改对其他事务的可见性
事务内的更改在 COMMIT 之前对其他会话不可见。这确保了隔离性。其他用户继续看到表的先前状态。
只有在 COMMIT 之后,所有更改才会同时变得可见,从而防止出现部分操作(如扣款)可见而另一部分(如存款)不可见的情形。
事务隔离级别
SQL 中的隔离级别名称为:读未提交、读已提交、可重复读和序列化。PostgreSQL 将读未提交视为读已提交,因此四个名称实际上提供三种不同的行为。读已提交是默认值;可以通过会话或数据库配置进行更改。
读已提交只允许查看查询开始前已提交的数据。可重复读提供事务开始时的一致快照。序列化模拟串行执行,但可能需要处理序列化错误。
使用保存点进行部分回滚
SAVEPOINT 允许在事务内创建一个回滚点,而不取消整个事务。这在复杂逻辑中很有用,当只需要撤销部分操作时。
例如,在向 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';
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00 WHERE name = 'Wally';
COMMIT;检查清单
- 在发出 BEGIN 之前,检查客户端事务和 autocommit 设置。
- 验证余额和受影响的行数;原子性本身并不能保证业务正确性。
- 在语句错误后,重新使用连接前应先发出 ROLLBACK。
- 保持事务简短,并根据应用程序策略重试序列化失败。
适用范围
对序列(sequences)的更改在 ROLLBACK 期间不会被回滚,并立即对其他事务可见。更高的隔离级别可能导致序列化错误,要求事务重试。