FATAL 53300:剩余连接槽保留用于非复制超级用户连接

G_H*_*hat 8 postgresql max-connections postgresql-12

我有一个在远程托管 VM 服务器 (Windows Server 2019) 上运行的 PostgreSQL 12.1 数据库系统(我将其称为 PGSQL)。几个月前我们升级了服务器操作系统和 PGSQL。从那时起,一切都或多或少地正常运行,直到今天早上,我开始在几乎每个连接到此 PGSQL 实例的内部应用程序中收到上述数据库错误。

为了检查连接,我运行了SELECT * FROM pg_stat_activity;,它返回了 103 行。我的postgresql.conf文件有max_connections = 100,所以这是有道理的,但没有意义的是,在这 103 个连接中,其中 90 多个连接被idle列为DISCARD ALL. 所有这些都显示为由同一非超级用户帐户从服务器自己的内部地址执行。但是,一些连接显示query_start一个月或更长时间前的日期值。

现在,不幸的是,我们现有的许多应用程序都是使用硬编码凭据构建的(我对继承的这些应用程序的代码有很多“清理”工作要做),并且通常是从指向的快捷方式执行的到托管 PGSQL 数据库的服务器上的“应用程序”共享,因此所有这些看起来都不是特别“可疑”。我试图简单地终止使用前一个查询中的值SELECT pg_cancel_backend(<pid>);之一的进程pid,但重新查询pg_stat_activity仍然在结果集中显示相同的记录(据我所知,所有值似乎完全相同)。

也许我没有使用正确的函数来终止这些“挂起”的进程或其他东西,但我无法弄清楚如何单独清除这些连接。因为我需要让我们的生产环境恢复到可用状态,所以我最终只是停止并重新启动服务器上的 PGSQL 服务,这确实清除了所有旧的DISCARD ALL语句,但我很好奇是否可以采取一些措施来防止这种积压的“挂”报表日后出现。

我的问题是,我怎样才能防止这种情况再次发生?需要注意的一件事是,在将 PGSQL 服务器升级到 v12.1 之前,我们运行了 v9.4 多年,并且从未遇到过此问题。我想知道是否有较新版本的 PGSQL 固有的问题,或者甚至可能是在 Windows Server 2019 环境中运行 PGSQL 的问题导致了此行为。


编辑

为了参考和整理,以下信息来自评论:

我没有任何服务器端用于管理连接池(我在有关PgBouncer的其他问题中看到了一些参考资料,但还没有机会查看它是否对我们的环境有帮助)。我的大多数应用程序都通过Npgsql库在连接字符串中实现池化。我已经构建了一个“通用库”来管理我的应用程序的连接 - 连接、断开连接、处置等 - 至少看起来工作正常。

然而,我继承的一些“遗留”代码当然可能没有正确实现,但这需要我花一些时间来挖掘代码以找到所有连接(有其中一些代码有很多问题)如果时间允许,我将调查该问题的潜在/可能根源。

如上所述,直到我们将 PGSQL 升级到 v12.1 并且相同的遗留代码已经存在好几年了,我才遇到这个问题。作为一名 1 人 IT 部门,服务器的重新启动“时间表”通常由我管理,由于其作为生产环境的性质,我很少重新启动服务器或重新启动 PGSQL 服务。希望我只是因为最近的升级而对一些事情过于敏感,而这整件事是我不会再看到的“一次性”情况。

我想是什么引发了我的问题,想知道为什么数据库没有转储这些已经闲置了一个月或更长时间的空闲连接。我会密切关注,如果问题仍然存在,我会研究更积极的连接池管理选项。


更新/编辑

不幸的是,自从 4 个月前发布这个问题以来,我一直没有主动检查空闲连接的状态。然而,今天早上,错误又出现了,我想起了这篇文章。再次执行,SELECT * FROM pg_stat_activity;显示超过 100 个连接(由我的服务器配置定义的最大连接数),其中超过 70 个idle/DISCARD ALL具有“旧”(几周/几个月)值backend_start/query_start日期值。

虽然知道数据库本身并不“管理”这些,但我仍然觉得奇怪的是,在运行以前版本的 PostgreSQL 的十多年里我从未遇到过这个错误。直到我们升级到v12之后,尽管环境基本保持不变。

无论如何,因为我有很多“遗留”应用程序可能会使这些连接保持打开状态,所以我决定在我的日常例程之一中实施检查,以防止此问题再次发生。我有一个每天运行的应用程序来复制和清理数据库中的某些信息。在该应用程序中,我添加了一个小函数,它将执行以下 SQL 命令,以保持后端清除这些“剩余”空闲连接:

SELECT
    pg_terminate_backend(pid)
FROM
    pg_stat_activity
WHERE
    pid <> pg_backend_pid()
    AND backend_start < (SELECT (NOW() - INTERVAL '2 WEEK')::DATE)
    AND state = 'idle';
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这应该查找任何idle超过 2 周的连接并终止它们。由于该应用程序每天运行,我相信这将有助于保持池尽可能干净。当然,如果有更好的方法 -不需要实施某些新的第三方解决方案(至少目前如此) - 我洗耳恭听,但是,目前,我相信这会“解决”我的问题。


其他参考/可能的替代解决方案

当我进一步查看时,我还在 StackOverflow 上发现了这个答案:

如何自动关闭PostgreSQL中的空闲连接?

链接的答案是指使用idle_in_transaction_session_timeoutPostgreSQL 9.6 中引入的设置。正如fresko的回答中所述,我应该能够使用发送到服务器的单个 SQL 命令来设置它,或者作为整个服务器的超级用户:

ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
Run Code Online (Sandbox Code Playgroud)

或针对每个会话的单个用户连接(如果需要)

SET SESSION idle_in_transaction_session_timeout = '5min';
Run Code Online (Sandbox Code Playgroud)

我在我的服务器上检查了此设置,它似乎已被禁用 ( 0),因此我也可能会考虑配置此设置。如果有人想要“跟随”,我将参考客户端连接默认值的 v12 文档页面。

jja*_*nes 4

无论您可能遇到什么问题,pg_cancel_backend都会取消后端的当前查询。但是空闲的后端没有当前查询,这就是它空闲的原因。你要pg_terminate_backend

更根本的是,您似乎遇到了应用程序问题或连接池问题,但您对它们的描述还不够,我们无法提供建议。

如果你只是想把事情踢得更远一点,你可以增加 max_connections 。拥有大量空闲连接(不是“事务中空闲”!)是不可取的,但也不是一个大问题。

在将 PGSQL 服务器升级到 v12.1 之前,我们运行了 v9.4 多年,但从未遇到过此问题。

那么0对1?这听起来不像是可以得出结论的样本量。也许 9.4 只是因为其他原因而经常重新启动,足以使泄漏得到控制。或者也许有人将 max_connections 增加到足够大的值,以至于在重新启动之间从未达到该值,但这种增加并没有在升级后继续存在。