代码改变世界

为什么"idle_session_timeout"的设置可能是个坏主意?

2024-01-15 12:24  abce  阅读(363)  评论(0编辑  收藏  举报

PostgreSQL 14 引入了参数 idle_session_timeout ,新参数让 DBA 可以轻松清除所有空闲连接。问题在于它可能造成的附带损害。

 

导致与连接有关的故障的常见因素

1. 不受限制的连接设置:

max_connections 和每个用户限制等连接设置会使系统遭受恶意拒绝服务(DoS)攻击。这些攻击会迅速耗尽可用连接,导致合法用户无法连接并中断系统功能。

2. 应用端连接池不够优化:

不够优化的应用端连接池会导致打开大量连接,而且这些连接可能一直处于打开和未使用状态,从而消耗宝贵的资源。这些连接会不断累积,最终耗尽服务器资源,导致系统反应迟钝。

3. 意外连接泄漏:

编程错误或应用程序逻辑错误也可能导致意外连接泄漏。这些泄漏会迅速耗尽可用连接池,影响系统性能,并可能导致系统崩溃。

4. 人为因素:

在理想情况下,必须严格限制和执行数据库的负载和连接数。这应该像航空安全一样严格审查。

5. 错误决定的组织因素:

在重大事故调查之后,通常会出现一系列令人惊讶的错误决定。常见的情况是,运行/DBA 开始负责管理空闲会话。用户群体对 "空闲"连接存在很多误解。我们应该记住,一个连接在建立后会立即处于"空闲"状态。当有 SQL 需要执行时,连接就会 "激活"。当处理结束后,它又会回到 "空闲"状态。来自任何连接池的大多数连接也会在大部分时间处于 "空闲"状态。因此,连接处于 "空闲"状态并没有什么问题。我想说的是,连接显示为 "空闲"并不意味着它可以被终止/杀死。

重要的是要看连接数。

 

连接管理不善的常见症状

·大量连接导致服务器性能逐渐下降

·服务器经常反应迟钝和/或崩溃

·服务器内存耗尽,PostgreSQL 发生 OOM

·服务器可用连接耗尽

·应用程序报告连接失败和连接意外中断

 

后端终止连接存在的问题

从后端终止连接有什么问题?通俗地说,这就好比银行关闭了你的账户,拿走了里面所有的钱。这对数据库客户端/应用程序来说可能是个"大惊喜"。大多数应用程序的设计和测试可能都无法处理这种意外情况。即使是 psql 也会有意外:

postgres=> l+
FATAL:  terminating connection due to idle-session timeout
FATAL:  server conn crashed?
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.

因此,除非应用程序的设计、开发和测试是为了处理意外连接中断,否则它将无法处理这种情况。大多数应用程序只会在报告错误后退出。在关键系统中,这会导致严重的业务中断。官方文档中对连接池或中间件的影响也做了提醒:

然而,用户有时仍然会忽视了这一影响。

 

从 PostgreSQL 社区的讨论来看,唯一支持该参数的有力论据是直接登录数据库执行自定义语句的一次性用户。他们可能会不小心让会话长时间处于打开状态,从而造成破坏。

 

有人讨论了如何使其更加明确,例如考虑为特定用户而不是服务器级别默认设置。由连接池管理或间接启动的客户端连接(例如由使用 postgres_fdw 的远程服务器启动的连接)可能不应包括在超时范围内。

 

 

如何避免造成严重后果?

解决办法不仅仅是技术问题,了解系统并实施适当的策略也很重要。

1.数据库账户和基于角色隔离

PostgreSQL 允许我们在不同级别和范围进行参数设置。对交互式登录账户的限制更为严格。为这些账户设置空闲会话超时(idle_session_timeout)等设置是非常可行的,最好不超过 5 分钟。最重要的是,idle_in_transaction_session_timeout 也不要超过几秒钟。

另一方面,我建议不要对服务账户使用 idle_session_timeout。不过,使用几分钟的 idle_in_transaction_session_timeout 也是可以接受的,因为这有助于我们发现有问题的应用程序逻辑。

 

2.使用网络超时

如果我们数据库怀疑连接被泄露,而应用程序却没有保留连接,还有其他方法。 在这种情况下,TCP 超时可能是最佳解决方案。PostgreSQL 有一个与 TCP 相关的超时参数选项,可以像其他 PostgreSQL 参数选项一样指定。

·tcp_keepalives_idle:该参数指定了不活动的秒数,在该秒数之后,PostgreSQL 服务器将向客户端发送一条 keepalive 消息。例如,如果我们将值设为 "120",Sever 将在不活动两分钟后向客户端发送一条 keepalive 消息,并等待回应。如果客户端确认,连接就会被验证。 如果客户端没有确认呢?这就需要下一个参数的帮助了

·tcp_keepalives_interval: 这是 TCP keepalive 信息重传的秒数,如果没有来自客户端的确认。例如,设置值为 20 会导致每 20 秒发送一次保持报文,直到获得确认。如果任何一次 keepalive 尝试都没有收到确认怎么办?这时就需要使用下一个参数了。

·tcp_keepalives_count: 这是客户端连接被视为 "死亡 "之前可能丢失的 keepalive 信息的数量。并终止连接。

因此,讨论的总体设置总结如下:

tcp_keepalives_idle = 120
tcp_keepalives_interval = 20
tcp_keepalives_count = 6

但用户可以根据环境需要调整这些值。

 

还有一个参数:tcp_user_timeout,需要注意的是,这个参数据说会影响其他参数的值。因此,我们不应该在一开始就考虑这个它。

 

从 PostgreSQL 14 开始,我们又多了一个选项:client_connection_check_interval(客户端连接检查间隔),它可以检测客户端连接是否消失。 如果客户端在长时间运行的语句(如 OLAP 系统的复杂报表查询)中死掉,这个选项就非常有用。 默认情况下,该检查是关闭的(值为 0)。如果预计会出现客户端连接中断的情况,将该值设置为 5 至 10 秒将非常有用。请注意,该值是以毫秒为单位指定的。PostgreSQL 甚至允许我们在用户级别设置这些值。

 

在TCP 如果发生超时,你可能会在 PostgreSQL 日志中看到如下条目:

LOG:  could not receive data from client: Connection timed out

 

总结

1.配置适当的连接数。将最大连接数和每个用户限制等连接限制设置为符合预期使用模式的合理值。定期检查并根据需要调整这些设置。PostgreSQL 实例的最大连接数设置不应超出可用 CPU 的 10 倍。

2.合理和正确的使用连接池

3.连接由创建者拥有。如果存在过多的空闲连接。需要修复导致创建连接的系统部分。从后端无声地终止连接不是解决办法,反而会招致更多问题。

4.除非拥有连接的应用程序/客户端提出请求,否则切勿从后端终止连接。

5.避免在全局级别使用 idle_session_timeout 等参数;仅在用户/角色级别使用,尤其是交互式登录账户。

6.避免在应用程序/服务账户中使用 idle_session_timeout。它可用于超时后果可预测的交互式登录账户。

7.使用前文提到的替代方案

8.监控连接数