46 BEGIN、COMMIT、ROLLBACK
事务将多条语句组合成一个全有或全无的单元。BEGIN(或 START TRANSACTION)启动事务;COMMIT 使变更永久生效;ROLLBACK 丢弃变更。在事务之外,每条语句都会在各自的隐式事务中运行——PostgreSQL 始终在事务中运行,你只需选择是否显式标记事务边界。事务让你能在多步变更中保持数据一致。
本章要点
- 使用
BEGIN开始事务,并以COMMIT或ROLLBACK结束 - ACID 属性(尤其是原子性和隔离性)
- 每条语句的隐式事务
- 隔离级别:
READ COMMITTED(默认)、REPEATABLE READ、SERIALIZABLE ABORT/ “事务已中止”状态
示例
PostgreSQL 提供完整的 ACID 保证:
- 原子性:事务中的所有变更要么全部生效,要么一项也不生效。
- 一致性:数据库会检查约束,并从一个有效状态转移到另一个有效状态。
- 隔离性:并发事务看不到彼此尚未提交的变更(严格程度因隔离级别而异)。
- 持久性:在正常持久性设置下,成功的
COMMIT会让已写入日志的变更在崩溃后仍然存在;synchronous_commit = off等设置会改变这一保证。
默认隔离级别是 READ COMMITTED:每条语句看到的是它开始执行时的数据库快照。REPEATABLE READ 为整个事务提供一个快照——重复读取会看到相同的数据。SERIALIZABLE 会增加对串行化异常的检查,并可能回滚那些会产生非串行调度的事务。
PostgreSQL 的事务错误模型很严格:事务内的任何错误都会中止该事务。后续语句会失败,并显示*“current transaction is aborted, commands ignored until end of transaction block”*,直到执行 ROLLBACK。要在事务中途优雅地处理错误,请使用保存点(下一课)。
本课特意插入一条会造成主键重复的记录,以演示事务失败。使用 psql 默认的
ON_ERROR_STOP=off运行它,这样后续的ROLLBACK和清理操作才能执行。其他课程可以使用-v ON_ERROR_STOP=1。
运行示例
运行方式:
常见陷阱
- 在交互式 psql 中忘记
COMMIT,意味着断开连接时所做的一切都会被回滚。如果希望显式控制,请设置\set AUTOCOMMIT off;默认的AUTOCOMMIT on会让每条语句在各自的隐式事务中运行。 - 长时间运行的事务会持有行锁,并阻止自动清理回收死元组。请保持事务短小。
- 在 PostgreSQL 中,把模式变更(DDL)和数据变更(DML)放在同一事务中没有问题——不同于 MySQL,DDL 也是事务性的。
- 在
READ COMMITTED下,同一事务中的两条语句可能看到同一行的不同值(如果并发事务在两条语句之间提交)。若要进行一致的多语句读取,请切换到REPEATABLE READ。
BEGIN; ... ROLLBACK; 可以在演练期间撤销事务性表变更,但它仍会获取锁并运行触发器。序列递增和外部副作用不会回滚。请使用临时数据库进行这些练习。
清理过程中脚本会删除 tx_demo。先使用 CREATE TABLE tx_demo (id integer PRIMARY KEY, val text); INSERT INTO tx_demo VALUES (1, 'a'); 重新创建它。然后打开两个 psql 会话。在会话 A 中:BEGIN; UPDATE tx_demo SET val = 'x' WHERE id = 1;(先不要提交)。在会话 B 中:SELECT * FROM tx_demo;——你应该看到旧值。然后在会话 A 中执行 COMMIT;,会话 B 的下一次 SELECT 将看到新值。
下载
SQL 文件:begin-commit-rollback.sql · 全部示例文件 · 准备工作