74

一些客户端连接到我们的 postgresql 数据库但保持连接打开。是否可以告诉 Postgresql 在一定数量的不活动后关闭这些连接?

TL;博士

如果您使用的是 Postgresql 版本 >= 9.2
THEN 使用我想出的解决方案

如果您不想编写任何代码,请
使用arqnid 的解决方案

如果您不想编写任何代码
并且您使用的是 Postgresql 版本 >=14
那么使用Laurenz Albe 的解决方案

4

7 回答 7

77

对于那些感兴趣的人,这里是我提出的解决方案,灵感来自Craig Ringer的评论:

(...)使用 cron 作业查看连接上次活动的时间(请参阅 pg_stat_activity)并使用 pg_terminate_backend 杀死旧的。(...)

选择的解决方案如下:

  • 首先,我们升级到 Postgresql 9.2。
  • 然后,我们安排一个线程每秒运行一次。
  • 当线程运行时,它会查找任何旧的非活动连接。
    • 如果连接状态为、或,则认为连接处于非活动状态。idleidle in transactionidle in transaction (aborted)disabled
    • 如果连接状态在 5 分钟以上保持不变,则连接被视为旧连接。
  • 还有其他线程与上述相同。但是,这些线程以不同的用户连接到数据库。
  • 我们为连接到我们数据库的任何应用程序保留至少一个连接。(rank()功能)

这是线程运行的 SQL 查询:

WITH inactive_connections AS (
    SELECT
        pid,
        rank() over (partition by client_addr order by backend_start ASC) as rank
    FROM 
        pg_stat_activity
    WHERE
        -- Exclude the thread owned connection (ie no auto-kill)
        pid <> pg_backend_pid( )
    AND
        -- Exclude known applications connections
        application_name !~ '(?:psql)|(?:pgAdmin.+)'
    AND
        -- Include connections to the same database the thread is connected to
        datname = current_database() 
    AND
        -- Include connections using the same thread username connection
        usename = current_user 
    AND
        -- Include inactive connections only
        state in ('idle', 'idle in transaction', 'idle in transaction (aborted)', 'disabled') 
    AND
        -- Include old connections (found with the state_change field)
        current_timestamp - state_change > interval '5 minutes' 
)
SELECT
    pg_terminate_backend(pid)
FROM
    inactive_connections 
WHERE
    rank > 1 -- Leave one connection for each application connected to the database
于 2015-06-11T00:16:15.490 回答
42

如果您使用的是 PostgreSQL >= 9.6,则有一个更简单的解决方案。假设您想每 5 分钟删除一次所有空闲连接,只需运行以下命令:

alter system set idle_in_transaction_session_timeout='5min';

如果您没有超级用户的访问权限(Azure 云上的示例),请尝试:

SET SESSION idle_in_transaction_session_timeout = '5min';

但是后者仅适用于当前会话,这很可能不是您想要的。

禁用该功能,

alter system set idle_in_transaction_session_timeout=0;

或者

SET SESSION idle_in_transaction_session_timeout = 0;

(顺便说一下,0 是默认值)。

如果使用alter system,则必须重新加载配置以开始更改并且更改是持久的,例如,如果您将重新启动服务器,则不必再重新运行查询。

检查功能状态:

show idle_in_transaction_session_timeout;
于 2018-10-08T14:28:13.457 回答
22

通过像PgBouncerserver_idle_timeout这样的代理连接,它会在几秒钟后关闭连接。

于 2012-09-13T09:59:10.440 回答
6

从 PostgreSQL v14 开始,您可以将idle_session_timeout参数设置为自动断开空闲的客户端会话。

于 2021-10-11T15:14:07.367 回答
5

如果您将 AWS 与 PostgreSQL >= 9.6 一起使用,则必须执行以下操作:

创建自定义参数组

转到 RDS > 参数组 > 创建参数组 选择您使用的 PSQL 版本,将其命名为“customParameters”或其他名称,并添加描述“处理空闲连接”。

更改 idle_in_transaction_session_timeout 值

幸运的是,它将创建默认 AWS 组的副本,因此您只需调整您认为不适合您的用例的内容。

现在单击新创建的参数组并搜索“空闲”。
'idle_in_transaction_session_timeout' 的默认值设置为 24 小时(86400000 毫秒)。将此数字除以 24 得到小时数 (3600000),然后您必须再次将 3600000 除以 4、6 或 12,具体取决于您是否希望超时分别为 15、10 或 5 分钟(或等效地乘以分钟数 x 60000,因此 5 分钟的值为 300 000)。

分配组

最后但并非最不重要的一点是,更改组:

转到 RDS,选择您的数据库并单击“修改”。

现在在“数据库选项”下,您将找到“数据库参数组”,将其更改为新创建的组。

然后,您可以决定是否要立即应用修改(注意停机时间)。

于 2020-07-17T19:05:02.463 回答
3

我遇到了连接被拒绝的问题,因为 Postgresql 12 服务器(但在使用早期 9.6 和 10 版本的类似项目上没有)和 Ubuntu 18 上连接了太多客户端。

我想知道这些设置是否

tcp_keepalives_idle 
tcp_keepalives_interval 

可能比

idle_in_transaction_session_timeout

idle_in_transaction_session_timeout确实只关闭来自失败事务的空闲连接,而不是其语句正确终止的非活动连接......文档显示这些套接字级别设置对 Unix 域套接字没有影响,但它可以在 Ubuntu 上运行。

于 2020-08-26T15:34:42.863 回答
1

直到 PostgreSQL 13,你可以使用我的扩展pg_timeout

于 2021-12-05T10:03:59.307 回答