Skip to content

事务自动提交 ​

自动提交(autocommit)指「每条语句被自动包进一个独立事务,执行完立即提交」。PostgreSQL 服务端默认就是这种模式:你敲一条 UPDATE,它就自动 BEGIN; UPDATE; COMMIT;,无需手动提交。所有客户端驱动与 ORM 的「事务开关」都建立在服务端这个行为之上——理解它才能明白为什么 psql 里一条语句失败了不用清理、而 JDBC 里忘记 commit() 数据就消失了。

前置知识

阅读本节前,建议先了解:BEGIN / COMMIT / ROLLBACK、事务的 ACID 特性

基础概念 ​

「自动提交」不是 PostgreSQL 的一个参数,而是服务端在没有显式事务块时的默认语义:

事实说明
服务端没有 autocommit 参数客户端协议层也不存在自动提交开关,它是「隐式事务」的通俗叫法
每条语句自带事务边界语句前隐式 BEGIN,成功后隐式 COMMIT,失败则隐式 ROLLBACK
BEGIN 一旦发出就进入显式事务此后语句不再立即提交,直到 COMMIT / ROLLBACK
自动提交是语句级原子性的来源单条语句内的部分修改不会「写一半」
事务块内失败会污染整个事务与自动提交模式不同,此时需 ROLLBACK 或保存点
DDL 也遵循同样规则CREATE TABLE / ALTER TABLE 可回滚(与 MySQL 的隐式提交不同)
部分命令禁止在事务块中执行VACUUM、CREATE INDEX CONCURRENTLY、REINDEX CONCURRENTLY 等
客户端可用 AUTOCOMMIT 变量接管psql 用 \set AUTOCOMMIT off,驱动用 setAutoCommit(false)

一个关键区分:服务端没有「关闭自动提交」的说法,只有「我开一个显式事务」。客户端所谓的「关闭自动提交」,本质是驱动在语句前替你发 BEGIN,在 commit() 时替你发 COMMIT:

text
服务端视角(客户端「关闭自动提交」时的实际报文流):

客户端 setAutoCommit(false)
   │
   ├─ 调 exec("UPDATE ...") → 驱动先发 BEGIN,再发 UPDATE
   ├─ 再调 exec("UPDATE ...") → 直接发 UPDATE(已在事务内)
   ├─ 调 commit()            → 驱动发 COMMIT
   └─ 若中途异常未回滚        → 连接上残留一个「悬挂事务」

语法与用法 ​

服务端层面没有 autocommit 语法,相关控制都发生在客户端:

text
-- psql:关闭自动提交(等价于在需要时自动 BEGIN)
\set AUTOCOMMIT off
\set AUTOCOMMIT on

-- psql:语句报错时自动建立隐式保存点,使单条语句失败不毁掉整个事务
\set ON_ERROR_ROLLBACK on
\set ON_ERROR_ROLLBACK interactive
\set ON_ERROR_ROLLBACK off

-- psql:遇错即停(脚本模式下非常重要)
\set ON_ERROR_STOP on

最简可运行示例——同一批语句,在自动提交开/关两种模式下的差别:

sql
-- 自动提交状态:每条语句独立提交
INSERT INTO t (id) VALUES (1);
INSERT INTO t (id) VALUES (2);
INSERT INTO t (id) VALUES (1);   -- 主键冲突,只回滚这一条
INSERT INTO t (id) VALUES (3);   -- 仍能成功
text
INSERT 0 1
INSERT 0 1
ERROR:  duplicate key value violates unique constraint "t_pkey"
DETAIL:  Key (id)=(1) already exists.
INSERT 0 1
sql
-- 显式事务中:一条失败会让整个事务进入 aborted 状态
BEGIN;
INSERT INTO t (id) VALUES (4);
INSERT INTO t (id) VALUES (1);   -- 冲突
INSERT INTO t (id) VALUES (5);   -- 错误!整个事务已作废
COMMIT;                          -- 实际等价于 ROLLBACK
text
BEGIN
INSERT 0 1
ERROR:  duplicate key value violates unique constraint "t_pkey"
DETAIL:  Key (id)=(1) already exists.
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ROLLBACK

详细说明 ​

服务端的隐式事务:每条语句都是一个事务 ​

PostgreSQL 服务端没有 autocommit 参数,可在 pg_settings 里搜不到它。它的实现方式是:当一个简单查询(simple query)到达且当前不处于事务块中时,后端把它包成「隐式事务」——执行前分配事务上下文,语句成功后提交。

sql
-- 确认服务端没有 autocommit 这个参数
SELECT name, setting FROM pg_settings WHERE name LIKE '%autocommit%';
text
 name | setting
------+---------
(0 rows)
sql
-- 观察隐式事务的效果:单条语句多行写入,只要一行失败整句都不生效
CREATE TABLE t (id int PRIMARY KEY);
INSERT INTO t VALUES (1), (2), (1), (3);
text
CREATE TABLE
ERROR:  duplicate key value violates unique constraint "t_pkey"
DETAIL:  Key (id)=(1) already exists.
sql
SELECT * FROM t ORDER BY id;   -- 空表:整条语句被回滚
text
 id
----
(0 rows)

这就是语句级原子性:INSERT INTO t VALUES (1),(2),(1),(3) 是一句,它要么四行全进,要么一行都不进。注意它不等于多语句原子性——想让多条语句一起成功或一起失败,必须显式 BEGIN。

sql
-- 通过协议层观察:一条扩展开的语句总在自己的事务里
SELECT pg_current_xact_id_if_assigned() AS xid;
text
 xid
-----
(1 row)

(返回 NULL 是因为单纯的 SELECT 不需要分配 XID——这也是 PostgreSQL 的惰性分配优化。)

psql 的 AUTOCOMMIT:一个纯客户端的「替你做 BEGIN」 ​

psql 提供了 AUTOCOMMIT 变量,默认 on。把它设成 off 后,psql 会在你第一次执行语句时自动发一条 BEGIN,你之后的语句都落在同一个事务里,直到你手动 COMMIT / ROLLBACK:

text
mydb=# \set AUTOCOMMIT off
mydb=*# INSERT INTO t VALUES (10);
INSERT 0 1
mydb=*# INSERT INTO t VALUES (11);
INSERT 0 1
mydb=*# \echo 注意提示符已变成 *,表示处在事务中
mydb=*# ROLLBACK;
ROLLBACK
mydb=# SELECT count(*) FROM t;
 count
-------
     0
(1 row)

要点:

  • 提示符 * 表示「事务中」(psql 默认的 %R 会显示:= 无事务、* 事务中、! 事务失败、? 状态不确定)。这是判断自己是否「悬挂在事务里」的最快方式。
  • \set AUTOCOMMIT off 不会立即发 BEGIN,而是等你的下一条语句。所以在 off 状态下不执行任何语句,事务并未开始。
  • AUTOCOMMIT on 时手动 BEGIN 依然有效:显式事务优先级更高,AUTOCOMMIT 只在「没有事务时」起作用。
text
mydb=# \set AUTOCOMMIT on
mydb=# BEGIN;
BEGIN
mydb=*# INSERT INTO t VALUES (20);
INSERT 0 1
mydb=*# COMMIT;      -- 手动结束,psql 不会自动提交
COMMIT

ON_ERROR_ROLLBACK:让自动提交模式下「单句失败不污染事务」 ​

这是 psql 最实用的开关之一。默认情况下,一旦进入事务块,任何语句报错都会让事务变成 aborted,后续语句全部报 25P02。\set ON_ERROR_ROLLBACK on 会让 psql 在每条语句前建立隐式保存点,语句失败时自动 ROLLBACK TO SAVEPOINT,于是单句失败不影响事务其余部分:

text
mydb=# \set ON_ERROR_ROLLBACK on
mydb=# BEGIN;
BEGIN
mydb=*# INSERT INTO t VALUES (1);
INSERT 0 1
mydb=*# INSERT INTO t VALUES (1);
ERROR:  duplicate key value violates unique constraint "t_pkey"
mydb=*# INSERT INTO t VALUES (2);   -- 依然成功
INSERT 0 1
mydb=*# COMMIT;
COMMIT

代价非常明确——每条语句都会创建子事务(对应一个隐式 SAVEPOINT)。在大量语句的脚本里开启它,会显著变慢,甚至触发子事务溢出。因此它的合理取值是:

取值行为适用场景
off(默认)不建立隐式保存点脚本、生产批处理
on每条语句前建保存点交互式调试
interactive仅在交互输入时建保存点,脚本文件不建日常开发(推荐)
text
mydb=# \set ON_ERROR_ROLLBACK interactive

ON_ERROR_STOP:脚本模式下的必备开关 ​

默认情况下 psql 遇到错误会继续执行剩余语句,并且最终退出码仍可能是 0。这在自动化脚本里非常危险——错误会被静默吞掉。\set ON_ERROR_STOP on 让 psql 在第一个错误时就中止并返回非零退出码:

bash
# 未设置 ON_ERROR_STOP:错误被吞掉,脚本「假装成功」
psql -d mydb -c "INSERT INTO t VALUES (1);"
psql -d mydb -c "INSERT INTO t VALUES (1);"   # 报错但退出码仍为 0
echo "exit code = $?"
text
exit code = 0
bash
# 推荐:脚本中显式开启,或用 -v 传入
psql -d mydb -v ON_ERROR_STOP=1 -f migrate.sql
echo "exit code = $?"
text
exit code = 1

也可以用 -1 / --single-transaction 让整个脚本在一个事务里执行,天然获得「要么全成、要么全败」的语义(要求脚本里不能有 VACUUM 这类禁止在事务块中执行的命令):

bash
psql -d mydb -v ON_ERROR_STOP=1 --single-transaction -f migrate.sql

JDBC:setAutoCommit(false) 与忘记 commit 的数据丢失 ​

JDBC 默认 autoCommit = true,与 PostgreSQL 服务端默认行为一致:每条 executeUpdate 后立即提交。为了让多条语句组成一个事务,需要显式关闭它:

java
// JDBC:标准的显式事务写法
try (Connection conn = dataSource.getConnection()) {
    conn.setAutoCommit(false);                 // 关闭自动提交
    try (PreparedStatement ps = conn.prepareStatement(
             "UPDATE accounts SET balance = balance - ? WHERE id = ?")) {
        ps.setBigDecimal(1, new BigDecimal("100.00"));
        ps.setLong(2, 1);
        ps.executeUpdate();

        ps.setBigDecimal(1, new BigDecimal("100.00"));
        ps.setLong(2, 2);
        ps.executeUpdate();

        conn.commit();                         // 必须显式提交
    } catch (SQLException e) {
        conn.rollback();                       // 失败必须回滚
        throw e;
    } finally {
        conn.setAutoCommit(true);              // 归还连接前恢复默认
    }
}

最容易踩的坑是忘记 commit():程序没有报错,但数据没了。原因是连接关闭时(或连接池归还时)PostgreSQL 会对未结束的事务执行隐式 ROLLBACK:

text
客户端               服务端
  │  setAutoCommit(false)
  │  UPDATE ...       ─────────►  BEGIN; UPDATE ...   (事务未结束)
  │  (未 commit)
  │  conn.close()     ─────────►  ROLLBACK            (数据被丢弃)

finally 中恢复 setAutoCommit(true) 同样重要:尽管 JDBC 规范说这会隐式提交当前事务,但不同连接池实现细节不一,依赖它提交是不可靠的——显式 commit() 才是唯一正确做法。

Python(psycopg):commit 是显式的,close 不等于提交 ​

psycopg(psycopg3)与 JDBC 差异很大:它默认 autocommit = False,也就是说所有语句默认落在一个事务里,必须手动 commit():

python
# psycopg3:默认就在事务中,必须显式提交
import psycopg

with psycopg.connect("dbname=mydb") as conn:
    with conn.cursor() as cur:
        cur.execute("UPDATE accounts SET balance = balance - %s WHERE id = %s", (100, 1))
        cur.execute("UPDATE accounts SET balance = balance + %s WHERE id = %s", (100, 2))
    conn.commit()          # 关键:不写这一句,with 块退出时会回滚

with psycopg.connect(...) 的语义是「退出时若未提交则回滚,然后关闭连接」——它不是自动提交。这与很多人从「with 就是自动保存」的直觉相反:

python
# 想用自动提交语义,必须显式开启
with psycopg.connect("dbname=mydb", autocommit=True) as conn:
    with conn.cursor() as cur:
        cur.execute("INSERT INTO t VALUES (1)")   # 立即生效
驱动默认提交行为提交方式
psql每条语句自动提交手动 BEGIN / COMMIT
JDBCautoCommit = truesetAutoCommit(false) + commit()
psycopg3autocommit = Falseconn.commit()
asyncpg隐式事务,需用 transaction() 上下文async with conn.transaction():
SQLAlchemy Core/ORM连接级 begin(),Session 管理事务session.commit()
python
# asyncpg:用事务上下文管理器,退出时自动 COMMIT / ROLLBACK
# async with conn.transaction():
#     await conn.execute("UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1)
#     await conn.execute("UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2)

ORM 与连接池:最常见的「悬挂事务」来源 ​

ORM(如 SQLAlchemy、Hibernate、Django ORM)通常在首次执行语句时开启事务,并依赖 commit() / rollback() / 会话关闭来结束它。连接池让问题变得更隐蔽:连接被归还到池里时事务未必已结束,下一个借用者就可能继承一个未结束的事务。

text
连接池中的悬挂事务(非常常见):

线程 1  借用连接 → UPDATE(事务开始)→ 业务异常 → 未 rollback → 归还连接
线程 2  借用同一连接 → SELECT(落在线程 1 的旧事务里!)
                       → 看到的是旧快照,且持有旧锁 → 诡异 bug

排查与治理手法:

sql
-- 查出所有「事务已开始但空闲」的后端
SELECT pid, state, xact_start, now() - xact_start AS xact_age,
       left(query, 60) AS last_query
FROM   pg_stat_activity
WHERE  state IN ('idle in transaction', 'idle in transaction (aborted)')
ORDER  BY xact_start;
text
 pid  |        state        |          xact_start           | xact_age | last_query
------+---------------------+-------------------------------+----------+----------------
 8421 | idle in transaction | 2026-09-28 10:00:01.123456+08 | 00:03:21 | UPDATE accounts SET ...
(1 row)
sql
-- 兜底:超时自动断开长时间空闲的事务
SET idle_in_transaction_session_timeout = '60s';
text
SET
sql
-- 也可以在 postgresql.conf / ALTER SYSTEM 中全局设置
-- ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';

要点:连接池归还连接前必须 rollback() 或 commit(),并对应用侧配置 idle_in_transaction_session_timeout 作为最后一道保险。

DDL 与自动提交:PostgreSQL 的优势 ​

这是 PostgreSQL 与 MySQL 的重要差异。MySQL 的 DDL 会隐式提交当前事务,导致「DDL 无法回滚」。PostgreSQL 的 DDL 是事务性的:CREATE TABLE、ALTER TABLE、DROP INDEX 等都可以放在事务里,失败或主动回滚时整体撤销。

sql
-- PostgreSQL:DDL 可以回滚
BEGIN;
CREATE TABLE tmp_orders (id bigint PRIMARY KEY);
ALTER TABLE tmp_orders ADD COLUMN amount numeric(12,2);
ROLLBACK;   -- 上面两张改动全部撤销,tmp_orders 不存在
text
BEGIN
CREATE TABLE
ALTER TABLE
ROLLBACK
sql
SELECT to_regclass('tmp_orders') IS NULL AS rolled_back;
text
 rolled_back
-------------
 t
(1 row)

这让「迁移脚本」可以做成单事务、失败自动回滚,是 PostgreSQL 运维上的明显优势。

不能在事务块里跑的命令 ​

有少数命令被明确禁止出现在事务块中(包括自动提交模式下的隐式事务不算问题,但显式 BEGIN 后执行会报错):

命令限制原因
VACUUM不能在事务中运行(VACUUM 必须在顶层执行)
CREATE INDEX CONCURRENTLY需要多阶段、非阻塞的独立提交点
DROP INDEX CONCURRENTLY同上
REINDEX CONCURRENTLY同上
CREATE DATABASE / DROP DATABASE不能在被访问的库的事务里执行
ALTER SYSTEM不能位于事务块或函数中
COMMIT PREPARED 等 2PC 命令需在事务外执行
sql
-- 错误!VACUUM cannot run inside a transaction block
BEGIN;
VACUUM t;
text
ERROR:  VACUUM cannot run inside a transaction block
sql
-- 错误!CREATE INDEX CONCURRENTLY cannot run inside a transaction block
BEGIN;
CREATE INDEX CONCURRENTLY t_id_idx ON t (id);
text
ERROR:  CREATE INDEX CONCURRENTLY cannot run inside a transaction block
bash
# 正确做法:让 psql 用自动提交模式逐条执行(不加 --single-transaction)
psql -d mydb -c "VACUUM (ANALYZE) t;"
psql -d mydb -c "CREATE INDEX CONCURRENTLY t_id_idx ON t (id);"

批处理的事务边界:批次大小怎么定 ​

批处理(bulk load、数据迁移、批量计算)的核心权衡是「事务大小」:

事务粒度优点缺点
每行一个事务(自动提交)无锁堆积、失败只影响一行每次都要等 WAL 刷盘,极慢
每 N 行一个事务吞吐与资源占用平衡需要选合适的 N
整个任务一个事务语义最简,天然全成或全败长事务:锁多、WAL 大、VACUUM 被阻塞、失败要全部重来
sql
-- 推荐:分批提交,兼顾吞吐与可控性
DO $$
DECLARE
  batch_size int := 10000;
  i int;
BEGIN
  FOR i IN 0..(SELECT count(*) / batch_size FROM staging) LOOP
    INSERT INTO target (id, payload)
    SELECT id, payload
    FROM   staging
    ORDER  BY id
    LIMIT  batch_size OFFSET i * batch_size;

    COMMIT;   -- 每批提交
  END LOOP;
END $$;
text
DO

如果导入过程中允许跳过坏行,则把「分批提交」与保存点(或 PL/pgSQL 异常处理)结合起来:

sql
-- 分批 + 逐行容错:坏行写入错误表,好行正常提交
DO $$
DECLARE r record;
BEGIN
  FOR r IN SELECT * FROM staging LOOP
    BEGIN
      INSERT INTO target (id, payload) VALUES (r.id, r.payload);
    EXCEPTION WHEN others THEN
      INSERT INTO import_errors (row_data, reason)
      VALUES (r::text, SQLERRM);
    END;
  END LOOP;
  COMMIT;
END $$;
text
DO

选择批次大小的经验法则:让单个事务在 1 秒到几十秒量级完成,且占用内存可控;一般在数千到几十万行之间试跑,用实际耗时与 pg_stat_activity 的 xact_start 时长来定。

实战示例 ​

场景:一条批量导入脚本,要求「坏行跳过、好行入库、出错即停、单批可回滚」。

第 1 步:准备环境与数据

sql
DROP TABLE IF EXISTS import_errors, target, staging;

CREATE TABLE staging (
  id      bigint PRIMARY KEY,
  payload text   NOT NULL
);

CREATE TABLE target (
  id      bigint PRIMARY KEY,
  payload text   NOT NULL
);

CREATE TABLE import_errors (
  id       bigserial PRIMARY KEY,
  row_data text NOT NULL,
  reason   text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);

INSERT INTO staging (id, payload) VALUES
  (1, 'a'), (2, 'b'), (3, 'c');
text
DROP TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 3

第 2 步:用自动提交模式试跑单条语句

bash
# 自动提交模式:每条 -c 是独立事务,一条失败不影响其它
psql -d mydb -v ON_ERROR_STOP=1 \
     -c "INSERT INTO target SELECT id, payload FROM staging WHERE id = 1;" \
     -c "INSERT INTO target SELECT id, payload FROM staging WHERE id = 1;" \
     -c "INSERT INTO target SELECT id, payload FROM staging WHERE id = 2;"
text
INSERT 0 1
ERROR:  duplicate key value violates unique constraint "target_pkey"
text
-- 注意:因为设置了 ON_ERROR_STOP=1,第三条不会执行

第 3 步:用显式事务 + 保存点做逐行容错

sql
BEGIN;

-- 第 1 行:正常
SAVEPOINT sp_row;
INSERT INTO target (id, payload) VALUES (1, 'a');
RELEASE SAVEPOINT sp_row;

-- 第 2 行:主键冲突,写入错误表后跳过
SAVEPOINT sp_row;
INSERT INTO target (id, payload) VALUES (1, 'a-dup');
ROLLBACK TO SAVEPOINT sp_row;
INSERT INTO import_errors (row_data, reason)
VALUES ('(1, a-dup)', 'duplicate key');

COMMIT;
text
BEGIN
SAVEPOINT
INSERT 0 1
RELEASE
SAVEPOINT
ERROR:  duplicate key value violates unique constraint "target_pkey"
DETAIL:  Key (id)=(1) already exists.
ROLLBACK
INSERT 0 1
COMMIT

第 4 步:验证结果

sql
SELECT * FROM target ORDER BY id;
SELECT row_data, reason FROM import_errors ORDER BY id;
text
 id | payload
----+---------
  1 | a
(1 row)

  row_data  |    reason
------------+--------------
 (1, a-dup) | duplicate key
(1 row)

第 5 步:清理

sql
DROP TABLE import_errors, target, staging;
text
DROP TABLE

注意事项 ​

  • 服务端没有 autocommit 参数,别去 pg_settings 找它。 自动提交是「无显式事务时每条语句独立成事务」这一语义的俗称。错误!认为「PG 支持 SET autocommit = 0」(MySQL 才有这个会话变量)。
sql
-- 错误!PostgreSQL 没有 autocommit 参数
-- SET autocommit = 0;
-- ERROR:  unrecognized configuration parameter "autocommit"
  • 自动提交模式下单句失败会回滚该句,但显式事务中会污染整个事务。 混用两者是 bug 温床。错误!在事务块里直接继续执行后续语句。
sql
BEGIN;
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (1);   -- 冲突
INSERT INTO t VALUES (2);   -- 错误!事务已 aborted
COMMIT;
  • \set ON_ERROR_ROLLBACK on 有性能代价。 它给每条语句加一个隐式保存点(子事务),脚本里大量语句时开销显著;日常开发用 interactive,批处理保持 off。
text
mydb=# \set ON_ERROR_ROLLBACK on      -- 不推荐用于大批量脚本
mydb=# \set ON_ERROR_ROLLBACK interactive   -- 推荐
  • 忘记提交/回滚就会留下悬挂事务。 客户端断开或连接归还时 PostgreSQL 会隐式 ROLLBACK,所以「没报错但数据没了」几乎总是漏写 commit()。
sql
-- 排查悬挂事务
SELECT pid, state, now() - xact_start AS age, left(query, 60)
FROM   pg_stat_activity
WHERE  state LIKE 'idle in transaction%';
  • VACUUM 与 * CONCURRENTLY 命令不能放进事务块,包括 psql 的 --single-transaction 模式。
bash
# 错误!--single-transaction 会让 VACUUM 失败
psql --single-transaction -c "VACUUM t;"
  • 不要依赖 JDBC 的 setAutoCommit(true) 来隐式提交。 规范上它有提交语义,实践上不可靠,显式 commit() 才是正解。

最佳实践 ​

  • 应用层统一用「显式事务」:驱动里明确 setAutoCommit(false) / autocommit=False,配合 try-catch-finally 保证提交或回滚,绝不留悬挂事务。
java
conn.setAutoCommit(false);
try {
    // ... 业务语句 ...
    conn.commit();
} catch (Exception e) {
    conn.rollback();
    throw e;
} finally {
    conn.setAutoCommit(true);   // 归还连接前恢复
}
  • 批处理用「固定批次大小 + 定期提交」,让每个事务在秒级完成;需要跳坏行时叠加保存点或 PL/pgSQL 异常处理。
sql
-- 每 10000 行提交一次
BEGIN;
INSERT INTO target SELECT id, payload FROM staging ORDER BY id LIMIT 10000;
COMMIT;
  • 脚本与迁移一律加 ON_ERROR_STOP=1,迁移脚本再叠加 --single-transaction(前提是脚本内无 VACUUM / CONCURRENTLY)。
bash
psql -v ON_ERROR_STOP=1 --single-transaction -f migration.sql
  • 给数据库加兜底超时,避免任何来源的悬挂事务长期持锁:idle_in_transaction_session_timeout + 语句超时 statement_timeout。
sql
ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
ALTER SYSTEM SET statement_timeout = '30s';
SELECT pg_reload_conf();
  • 交互调试时用 psql 提示符确认自己是否在事务里(= 无事务、* 事务中、! 已失败),避免中途 CTRL-C 后残留事务。
text
mydb=#   ← 无事务(=)
mydb=*#  ← 事务中(*),离开前记得 COMMIT 或 ROLLBACK
mydb=!#  ← 事务已失败(!),只能 ROLLBACK 或 ROLLBACK TO SAVEPOINT

下一节

继续学习:隐式与显式事务

参考链接 ​