Chris van Eijk:PostgreSQL 中“事务中空闲”会话:它持有什么

Chris van Eijk:PostgreSQL 中“事务中空闲”会话:它持有什么

💡 原文英文,约1500词,阅读约需6分钟。
📝

内容提要

PostgreSQL中“事务中空闲”会话虽不执行语句,但仍持有锁和连接槽,并可能阻止VACUUM清理死行。仅执行SELECT的会话在READ COMMITTED隔离级别下会阻塞ALTER TABLE并连带阻塞后续查询;REPEATABLE READ或已写入的会话还会阻碍清理。该超时默认值为0,可按角色设置idle_in_transaction_session_timeout,超时后连接被终止、事务回滚。

🔎

延伸解读

只读事务也可能阻塞DDL和后续查询

文章实测显示,在READ COMMITTED隔离级别下,仅执行SELECT的“事务中空闲”会话仍持有ACCESS SHARE锁。这足以让ALTER TABLE等待直至超时,而排队等待该ALTER的普通SELECT也会被连带阻塞。因此,一个忘记提交的只读事务加上一次迁移,就可能让整张表的读取暂时不可用。

VACUUM受阻取决于隔离级别与是否写入

文章澄清了常见说法:并非所有空闲事务都阻碍清理死行。READ COMMITTED下只读会话在语句间不保留快照,VACUUM可正常移除死行;REPEATABLE READ会保留快照,或会话已执行写入(持有backend_xid),则死行无法清理,直到事务结束。排查时可关注backend_xmin和backend_xid字段。

超时设置需区分作用范围与连接后果

idle_in_transaction_session_timeout仅针对等待客户端的空闲事务,超时后连接被终止、事务回滚,应用需重连。transaction_timeout更严格,还会终止运行中的长语句,文档不建议全局设置。两者默认均为0。可按角色设置,但只对新会话生效,已连接的会话不受影响。

❓

Q&A

PostgreSQL 里 idle in transaction 状态是什么意思?

idle in transaction 表示会话已经开启事务、执行完最后一条语句,正在等待客户端发送下一条命令,但事务尚未提交或回滚。此时服务器不执行任何查询,但事务仍然打开,它持有的锁、连接槽等资源不会被释放。

一个只执行了 SELECT 的 idle in transaction 会话会阻塞什么?

在 READ COMMITTED 隔离级别下,只执行 SELECT 的会话会持有表的 ACCESS SHARE 锁,这会阻塞 ALTER TABLE 等需要排他锁的操作;而等待 ALTER TABLE 的普通 SELECT 也会被连带阻塞。此外,该会话还占用一个连接槽。

idle in transaction 会话在什么情况下会阻止 VACUUM 清理死行?

当会话在 REPEATABLE READ 隔离级别下执行过 SELECT(持有快照),或者会话执行过写操作(持有事务 ID)时,即使 backend_xmin 为空,VACUUM 也无法清理其他会话产生的死行,直到该事务结束。而在 READ COMMITTED 下仅执行 SELECT 的会话不会阻止 VACUUM。

如何查找数据库中处于 idle in transaction 状态的会话?

可以查询 pg_stat_activity,筛选 state 为 'idle in transaction' 或 'idle in transaction (aborted)' 的会话,并查看 pid、usename、application_name、事务年龄、空闲时长、backend_xid、backend_xmin 以及最后执行的查询(query 字段)。backend_xmin 或 backend_xid 非空表示该会话正在阻止 VACUUM。

idle_in_transaction_session_timeout 超时后会发生什么?

当会话空闲在事务中超过设定时间,连接会被终止,并报错 FATAL: terminating connection due to idle-in-transaction timeout (SQLSTATE 25P03),当前事务回滚。该参数默认值为 0(禁用),可按角色设置,例如 ALTER ROLE app SET idle_in_transaction_session_timeout = '30s',仅对新会话生效。

transaction_timeout 和 idle_in_transaction_session_timeout 有什么区别?

transaction_timeout 是 PostgreSQL 17 引入的参数,会终止整个事务(包括正在运行的语句),报错 SQLSTATE 25P04;而 idle_in_transaction_session_timeout 仅在会话空闲等待客户端时触发,报错 SQLSTATE 25P03。两者都会终止连接,但 transaction_timeout 更严格,文档建议不要在 postgresql.conf 中全局设置。

🏷️

标签

➡️

继续阅读