【问题标题】:How to close idle connections in PostgreSQL automatically?如何自动关闭 PostgreSQL 中的空闲连接?
【发布时间】:2012-09-05 15:36:35
【问题描述】:

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

TL;DR

如果您使用的是 Postgresql 版本 >= 9.2
然后使用the solution I came up with

如果您不想编写任何代码
然后使用arqnid's solution

如果您不想编写任何代码
并且您使用的是 Postgresql 版本 >= 14
然后使用Laurenz Albe's solution

【问题讨论】:

  • 您可以使用 cron 作业查看连接上次处于活动状态的时间(请参阅 pg_stat_activity)并使用 pg_terminate_backend 杀死旧的。在简单的查询中轻松表达。不过,我不确定 pg_terminate_backend 在相当古老的 8.3 中是否可用。
  • 我在使用 C3P0 池的服务中遇到了类似的问题 - 所有连接都已关闭(最终阻塞)但在我进行负载测试后,负载后空闲连接的数量没有下降测试完毕。在pg_stat_activity 表中,我有很多idle 连接正在运行SELECT 1SHOW TRANSACTION ISOLATION LEVEL。然后当我运行第二次负载测试时,这个数字一直在增长,这是否意味着在第二次测试期间没有重新使用空闲连接?我可以在这里使用相同的解决方案来修复它吗?
  • @Zip 你能把你的评论变成一个新问题,并在这里放一个指向这个新问题的链接吗?我会更容易回答你。
  • @Stephan 问题在这里:stackoverflow.com/questions/51682584/…。谢谢!

标签: postgresql timeout connection


【解决方案1】:

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

【讨论】:

  • 对于 Pg13 及更高版本,您的扩展会发生什么?
  • 我已经用 PG13 测试了我的扩展,并且我刚刚用 PG 14 成功测试了它:根据我的测试,它按预期工作。
【解决方案2】:

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

【讨论】:

    【解决方案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 上运行。

    【讨论】:

    • 想试试并在这里与我们分享您的结果吗? ;)
    • 嗨,@Stefan,它似乎有效。我没有看到昨天与 pg_stat_activity 的任何联系。我使用了以下设置:tcp_keepalives_interval=300 tcp_keepalives_interval=30。如果我理解正确,服务器应该等待 5 分钟以检查客户端是否仍然连接,然后再等待 30 秒等待来自客户端的 ACK...
    • 我还必须说,我用应用程序中的临时连接替换了持久 PDO 连接。它们导致了客户端插槽未被释放的问题。
    • 嗨,@FranckTheeten 你能说出确切的设置吗?在 cmets 中,您两次提到 tcp_keepalives_interval...
    • 嗨,抱歉打错了,我启用了tcp_keepalives_idle=300tcp_keepalives_interval=30tcp_user_timeout = 300(在单独的行上)。服务器在向客户端发送第一个 ACK​​ 请求之前等待 5 分钟,如果没有回复,则等待 x 30 秒再重试,并在 5 分钟后关闭连接槽。 tcp_keepalives_count 还允许指定保活查询的数量(如果未提及,则默认系统设置 TCP_KEEPCNT)。另见这个非官方网站:postgresqlco.nf/doc/en/param/tcp_keepalives_count
    【解决方案4】:

    如果您将 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,选择您的数据库并点击“修改”。

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

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

    【讨论】:

    【解决方案5】:

    对于那些感兴趣的人,这是我提出的解决方案,灵感来自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
    

    【讨论】:

    • 最后,我们迁移到 Postgresql 9.2 以利用 pg_terminate_backend。我们使用类似 cron 的作业来定期调用 pg_terminate_backend
    • 你能发布一个你使用的脚本吗?
    【解决方案6】:

    如果您使用的是 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;
    

    【讨论】:

    • 有趣的功能。但是,它不允许精细的连接选择(保持一个连接处于活动状态,将一些应用程序连接列入白名单......)
    • 也许它有可能写一个白名单,但我不确定。该命令对我来说也是新的。
    • 这只会关闭打开事务但未能在给定超时内关闭(提交或回滚)它的连接(正如名称“idle_in_transaction_session_timeout”所暗示的那样)。它不会关闭只是“空闲”的连接。
    • "idle_in_transaction_session_timeout" 也可以在postgresql.conf中设置
    • 如果我把 session time out = 0 ,它会影响我正在运行的查询吗?
    【解决方案7】:

    通过PgBouncer 之类的代理进行连接,该代理将在server_idle_timeout 秒后关闭连接。

    【讨论】:

    • pg pool II 可以做同样的事情吗?
    • @Stephan 谢谢!如果您没有在评论中标记我,我会错过答案。
    • 自 v14 以来,Posgtresql 终于有了与 PgBouncer server_idl_timeout 等价的功能。见stackoverflow.com/a/69528572/363573
    猜你喜欢
    • 2017-10-31
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多