首页 / PostgreSQL 入门教程 / 事务控制(BEGIN/COMMIT/ROLLBACK/SAVEPOINT)

PostgreSQL 入门教程

事务控制(BEGIN/COMMIT/ROLLBACK/SAVEPOINT)

本教程共 50 篇 · 第 46 篇 · 更新于 2026-07-31 · 约 7 分钟阅读

PostgreSQLPostgreSQL 入门教程事务BEGINCOMMITSAVEPOINT

46. 事务控制(BEGIN/COMMIT/ROLLBACK/SAVEPOINT)

本节目标:理解事务的 ACID 特性,掌握显式事务、回滚与保存点的写法,以及出错时如何处理。

什么是事务

事务(TRANSACTION)把多条 SQL 打包成一个”要么全成、要么全不成”的整体。比如转账:A 扣钱、B 加钱,两步必须一起成功,不能只成一半。

PostgreSQL 默认每条语句自己就是一个事务(自动提交)。想多句绑一起,就用手动事务。

ACID 四特性

  • 原子性(Atomicity):全做或全不做。
  • 一致性(Consistency):数据始终满足约束。
  • 隔离性(Isolation):并发事务互不干扰。
  • 持久性(Durability):提交后数据永久保存。

自动提交

你平时在 psql 里敲一条 UPDATE,回车就生效,背后其实是 PostgreSQL 自动帮你包了一层 BEGIN ... COMMIT。这叫”自动提交”(autocommit)。

在 psql 里可以用 \set AUTOCOMMIT off 关掉自动提交,之后每条语句都得自己 COMMIT 才生效。调试时很有用,但别忘了提交,否则改动只对当前会话可见、连接断开就丢了。

显式事务:BEGIN / COMMIT

BEGIN 开头, COMMIT 收尾:

postgres=# BEGIN;
postgres=# UPDATE accounts SET balance = balance - 100 WHERE id = 1;
postgres=# UPDATE accounts SET balance = balance + 100 WHERE id = 2;
postgres=# COMMIT;

只有 COMMIT 之后,改动才真正生效并对外可见。COMMIT 之前,别的会话看不到你的修改。

回滚:ROLLBACK

中途发现不对,用 ROLLBACK 全部撤销,回到事务开始前的样子:

postgres=# BEGIN;
postgres=# DELETE FROM orders WHERE id = 999;
postgres=# ROLLBACK;   -- 删除被撤销,数据还在
Tip

没写 BEGIN 的单条 SQL,PostgreSQL 会自动包一层 BEGIN … COMMIT,这叫”自动提交”。所以平时你改了数据不显式提交也生效,背后是它在干活。

保存点:SAVEPOINT

保存点(SAVEPOINT)让你在事务里设”checkpoint”,只回退到某一点,前面的改动保留。

postgres=# BEGIN;
postgres=# UPDATE accounts SET balance = balance - 100 WHERE id = 1;
postgres=# SAVEPOINT sp1;
postgres=# UPDATE accounts SET balance = balance + 100 WHERE id = 2;
postgres=# -- 发现该加给 3 号,回退到 sp1
postgres=# ROLLBACK TO sp1;
postgres=# UPDATE accounts SET balance = balance + 100 WHERE id = 3;
postgres=# COMMIT;

ROLLBACK TO sp1 之后,sp1 还在,可以反复回退到它。用不到了可以 RELEASE SAVEPOINT sp1 释放。

Note

保存点可以嵌套:sp1 里再建 sp2, ROLLBACK TO sp1 会把 sp2 一起撤掉。深层保存点常用来保护”这一段操作”,失败时只退这一段。

出错时整个事务中止

这是新手最容易踩的坑:事务里只要有一条语句报错,整个事务就进入”中止状态”,后面所有语句都会报 current transaction is aborted

postgres=# BEGIN;
postgres=# UPDATE accounts SET balance = balance - 100 WHERE id = 1;
postgres=# UPDATE accounts SET balance = balance / 0 WHERE id = 2;  -- 除零报错
postgres=# UPDATE accounts SET balance = balance + 100 WHERE id = 3;  -- 也报错:事务已中止
postgres=# ROLLBACK;  -- 只能用 ROLLBACK 整体撤掉
Warning

报错导致事务进入中止状态时,只有 ROLLBACK TO 保存点 或整体 ROLLBACK 能救回来,不能接着往下写普通语句。所以长事务里要善用 SAVEPOINT,出错就 ROLLBACK TO 到最近的保存点继续。

设置隔离级别与只读

事务开始后可以设定隔离级别和读写模式:

postgres=# BEGIN;
postgres=# SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
postgres=# SET TRANSACTION READ ONLY;
postgres=# SELECT * FROM accounts;  -- 只读事务,不能改数据
postgres=# COMMIT;

隔离级别有 READ UNCOMMITTED(PG 里等同于 READ COMMITTED)、READ COMMITTED(默认)、REPEATABLE READ、SERIALIZABLE。具体差异见第 47 章。

保存点的细节:回退后还在吗

ROLLBACK TO sp1 只撤回到 sp1 那个点,sp1 本身依然有效,你可以再往后改、再回退,反复横跳。只有 RELEASE SAVEPOINT sp1 才会把它彻底释放,之后不能再 ROLLBACK TO sp1

嵌套保存点也很常见:在 sp1 里再建 sp2,回退到 sp1 会连 sp2 一起消失。这其实就是”子事务”的实现机制。

长事务的风险

事务开得越久,风险越大:

  • 占着的锁一直不释放,别的会话容易被卡住。
  • 期间产生的旧版本行不能被 VACUUM 清理,表会膨胀。
  • 连接意外断开,整个事务回滚,前面白干。
Warning

别在事务里做”等用户确认”之类的人机交互。曾经有人开事务后去吃午饭,回来发现整库写入都堵住了。长事务要短平快。

事务内临时改参数:SET LOCAL

事务里想临时改个参数(比如只在这段事务里放宽某个限制),用 SET LOCAL。事务一提交/回滚,参数自动还原:

postgres=# BEGIN;
postgres=# SET LOCAL work_mem = '64MB';  -- 只对本事务有效
postgres=# -- 跑一段吃内存的查询
postgres=# COMMIT;  -- 之后 work_mem 恢复默认值

只读事务的用途

设成只读的事务,能防止误改数据,也方便做一致的报表快照:

postgres=# BEGIN;
postgres=# SET TRANSACTION READ ONLY;
postgres=# SELECT * FROM orders;  -- 可以查
postgres=# UPDATE orders SET amount = 0;  -- 报错:只读事务
postgres=# COMMIT;

应用里的事务

你在代码里用的 ORM / 驱动,本质也是这套:BEGIN 对应”开事务”,commit() 对应 COMMIT,抛异常时框架帮你 ROLLBACK。理解这一章,就能看懂代码里”事务没提交导致数据没存”之类的坑。

事务隔离级别预告

本章只讲了怎么开关事务。PostgreSQL 还有四种隔离级别(READ COMMITTED 默认、REPEATABLE READ、SERIALIZABLE 等),它们决定并发时你能看到别的会话的哪些改动。这块内容多、坑也多,下一章(第 47 章)会专门展开。这里先有个概念:事务不只是”提交或回滚”,还关系到”并发下能看到什么”。

面试常问:事务到底解决了什么

如果被人问”为什么需要事务”,记住 ACID 四个字就够展开:原子性保证要么全成要么全不成(转账不会钱丢了),一致性保证不破坏约束,隔离性保证并发互不干扰,持久性保证提交后不丢。日常写代码,把”必须一起成功”的多步操作包进 BEGIN ... COMMIT,就是事务最朴素也最有用的用法。

常见误区

  1. 以为 ROLLBACK TO 之后事务还能像没出错一样继续写。可以,但前提是没进入”中止状态”;一旦中止只能 ROLLBACK TO 保存点 或整体 ROLLBACK
  2. 忘了 COMMIT,在 psql 里关掉自动提交后改动一直没保存。
  3. 把多步操作不包事务,结果中途失败,前面的改动了、后面的没动,数据不一致。
  4. 在事务块里 CALL 一个带内部 COMMIT 的存储过程,直接报错(见第 45 章)。
  5. 误以为 ROLLBACK 能回退已经 COMMIT 的事务。COMMIT 之后就永久了,撤不回。