最近在浏览 guc_table.c 时注意到 PostgreSQL 有很多与超时相关的参数可供用户设置,在这些参数中,我想介绍三个我认为最常用的参数:

  • statement_timeout: Sets the maximum allowed duration of any statement.
  • idle_in_transaction_session_timeout: Sets the maximum allowed idle time between queries, when in a transaction.
  • idle_session_timeout: Sets the maximum allowed idle time between queries, when not in a transaction.

statement_timeout

PostgreSQL 中长事务往往会带来一些问题,比如 table bloat(由于旧版本记录不能及时回收)、占用资源(锁、内存)等,因此有些实例会设置一个合理的 statement_timeout 来自动杀死运行时间过长的查询。

postgres=# set session statement_timeout = '10s';
SET
postgres=# select pg_sleep(1000);
ERROR:  canceling statement due to statement timeout
postgres=#

但这个参数对于 idle in transaction 的 session 不起作用,比如:

postgres=# set application_name = 'pg-x';
SET
postgres=# set session statement_timeout = '10s';
SET
postgres=# create table t1(id int);
CREATE TABLE
postgres=# insert into t1 values(1),(2);
INSERT 0 2
postgres=# begin;
BEGIN
postgres=*# update t1 SET id=2 where id=1;
UPDATE 1

开启事务但一直不 commit,该 session 一直处于 idle in transaction 状态,在另一个客户端查询:

postgres=# select pid, application_name, xact_start, query_start, state from pg_stat_activity where application_name='pg-x';
  pid   | application_name |          xact_start           |          query_start          |        state
--------+------------------+-------------------------------+-------------------------------+---------------------
 201736 | pg-x             | 2023-10-14 16:09:10.389653+00 | 2023-10-14 16:10:17.354243+00 | idle in transaction
(1 row)

idle_in_transaction_session_timeout

idle_in_transaction_session_timeout 是一个用于设置事务在空闲状态下超时时间的参数,当一个事务处于空闲状态(没有活动查询)并且超过了指定的时间限制时,PostgreSQL 将自动终止该事务并释放相关资源。

postgres=# set session idle_in_transaction_session_timeout = '10s';
SET
postgres=# select pg_backend_pid();
 pg_backend_pid
----------------
         201736
(1 row)

postgres=# begin;
BEGIN
postgres=*# commit;
FATAL:  terminating connection due to idle-in-transaction timeout
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
postgres=# select pg_backend_pid();
 pg_backend_pid
----------------
         202340
(1 row)

可以看到在 begin 命令执行 10s 之后再去执行 commit,客户端当前连接的 backend 已经被杀死了,并重新连接了另一个 backend。

idle_session_timeout

idle_session_timeout 是 PG14 引入的一个参数,当 backend 一直没有查询(处于 idle 状态)超过参数设定的时间时,进程会被杀死。

postgres=# select pg_backend_pid();
 pg_backend_pid
----------------
         202340
(1 row)

postgres=# set session idle_session_timeout = '10s';
SET
postgres=#
postgres=# \d
FATAL:  terminating connection due to idle-session timeout
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.
postgres=# select pg_backend_pid();
 pg_backend_pid
----------------
         202453
(1 row)

在设置了 idle_session_timeout 之后等待 10s,然后随便执行一个命令,即可验证当前的后端进程已被杀死。

小结

本文介绍了三个常用的超时参数,根据实际情况进行合理设置能让你的系统运行地更稳健。另一个 lock_timeout 用于设置事务在等待锁资源时的超时时间,当一个事务等待获取锁资源的时间超过指定的时间限制时,PostgreSQL 会自动中断该事务,但这个我用的不多,所以未放在正文中。