生产 PostgreSQL 连接数耗尽:从连接池、长事务到数据库参数的排查与恢复验证

AI智能摘要
生产环境 PostgreSQL 连接数逼近上限时,不应先重启数据库或盲目提高 max_connections,而应确认瓶颈在应用连接池、连接代理还是数据库端。结合连接数、会话状态、事务时长和等待事件,利用 pg_stat_activity 按应用、用户及来源定位空闲连接、长事务与异常占用;核算多进程、多实例及后台任务的池容量,并检查连接归还、代理队列和近期变更。依据故障范围调整并发或配置,验证连接、等待与请求错误是否恢复,避免扩容将压力转移至数据库。
— 此摘要由AI分析文章内容生成,仅供参考。

生产环境出现 PostgreSQL 连接数持续逼近上限、应用请求排队或新连接失败时,先不要直接重启数据库或提高 max_connections。先确认连接卡在应用连接池、连接代理还是数据库端,再结合会话状态、事务时长和资源余量选择处置方式;否则扩容可能只是把压力转移到数据库,甚至增加故障影响面。

先确认影响范围和故障位置

先记录告警开始时间、受影响的应用和接口、连接失败或超时错误、请求排队情况,以及数据库连接数变化。同步查看应用连接池的已用、空闲、等待连接数和获取连接超时;若使用 PgBouncer 等连接代理,还要查看代理侧的客户端连接、服务端连接和等待情况。

观察到的现象优先检查初步判断
应用连接池等待数上升,但数据库连接数明显低于上限应用池配置、连接获取耗时、连接泄漏、池内等待连接可能卡在应用侧,未必是数据库连接槽位耗尽
数据库连接数接近上限,同时应用创建新连接失败数据库会话分布、连接来源、空闲连接和长事务数据库端可用连接槽位不足
应用池和数据库连接数都高,且请求排队各应用实例的池上限、实例数量、代理池配置多层连接容量叠加,或并发需求超过数据库承载能力
数据库连接数不高,但应用请求仍超时池等待、网络、认证、连接建立耗时及应用错误连接上限可能不是根因

如果只有部分应用受影响,先比较它们使用的连接串、实例数量、连接池配置和发布变更;若多个应用同时受影响,再优先检查共享的数据库或连接代理。区分“数据库拒绝新连接”和“应用池拿不到连接”尤其重要:两者都可能表现为请求超时,但处置位置不同。

检查数据库连接数和会话状态

在仍能连接数据库时,先查看连接上限和当前会话分布:

SHOW max_connections;

SELECT
    count(*) AS total_sessions,
    count(*) FILTER (WHERE state = 'active') AS active,
    count(*) FILTER (WHERE state = 'idle') AS idle,
    count(*) FILTER (WHERE state = 'idle in transaction') AS idle_in_transaction
FROM pg_stat_activity;

再按数据库、用户、应用名和会话状态拆分,找出连接主要来自哪里:

SELECT
    datname,
    usename,
    application_name,
    client_addr,
    state,
    count(*) AS sessions
FROM pg_stat_activity
GROUP BY datname, usename, application_name, client_addr, state
ORDER BY sessions DESC;

pg_stat_activity 的可见范围可能受权限影响。若结果与监控平台或数据库日志不一致,确认查询账号是否有权查看所需会话信息;同时留意不同 PostgreSQL 版本及部署方式对保留连接、复制连接等的处理差异,不要仅凭一个总数推断所有连接槽位都可供普通应用使用。

空闲连接不一定是故障:连接池通常会保留一定数量的空闲连接以复用。但当多个应用、多个实例各自维护连接池时,池上限会累加;连接池配置看似合理,也可能在实例扩容、任务进程增加或应用发布后超过数据库承载能力。

按会话状态识别空闲连接与长事务

查看活跃会话、事务开始时间、等待事件和当前查询:

SELECT
    pid,
    datname,
    usename,
    application_name,
    client_addr,
    state,
    wait_event_type,
    wait_event,
    clock_timestamp() - xact_start AS transaction_age,
    clock_timestamp() - query_start AS query_age,
    left(query, 300) AS query_excerpt
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
ORDER BY xact_start NULLS LAST, query_start NULLS LAST;

重点关注以下情况:

  • 大量 idle 会话:可能是连接池正常保留,也可能是池上限过大、应用实例过多,或连接未按预期释放。结合应用池的空闲连接数和数据库连接来源判断。
  • 长时间 idle in transaction:客户端仍保持事务打开,但当前没有执行查询。检查应用是否遗漏提交或回滚、是否在事务中等待外部服务,或异常路径没有清理事务。此类会话可能长期占用连接,并影响锁和数据清理。
  • 长时间 active 会话:核对其持续时间、等待事件和应用请求。连接数问题中,重点是它是否长期占用连接、是否与请求堆积同时出现;不要将排查扩展成无关的 SQL 优化。
  • 来源集中于特定应用或地址:优先检查对应服务的实例数、工作进程数、连接池上限和近期发布变更。

query_start 和 xact_start 的含义不同:前者用于观察当前查询持续时间,后者用于观察事务持续时间。事务开始时间较早不等于一定有问题,应结合会话状态、业务行为和影响判断。查询内容也可能包含敏感信息,排障输出应按生产数据要求处理。

逐层缩小范围

应用层:确认连接是否被正确归还

检查连接获取与释放是否成对,异常、超时、取消和事务回滚路径是否仍会归还连接。核对应用连接池的最大连接数、空闲连接数、获取连接超时、空闲回收策略,以及进程内是否创建了多个独立连接池。

将配置上限按所有运行单元加总,而不是只看单个进程:

应用总连接需求 ≈ 单个进程的池上限 × 进程数 × 应用实例数,再加上定时任务、后台消费者、管理任务等额外连接。

这是容量估算,不代表数据库一定会同时收到这些连接;但如果估算值已接近数据库可用连接容量,就需要先调整并发和池配置,而不是继续扩实例。扩容应用实例时,要把新实例带来的连接池增量纳入评估。

连接池或代理层:区分“客户端排队”和“服务端连接不足”

检查连接池自身的已用、空闲、等待数、等待时长和超时数。若有 PgBouncer,可在其管理控制台使用 SHOW POOLS、SHOW CLIENTS、SHOW SERVERS 等命令查看池内队列与服务端连接;具体可用命令和字段以部署版本为准。

  • 客户端等待增加、服务端连接接近配置上限时,检查代理池上限、数据库端连接余量和请求并发。
  • 服务端连接不多而客户端仍大量等待时,检查代理池配置、路由和服务端连接复用情况。
  • 应用池等待高而代理和数据库都不满时,检查应用池是否过小、连接是否被长时间占用,或连接获取是否被其他应用逻辑阻塞。

若考虑切换连接池模式,先确认应用是否依赖会话级状态、临时对象或其他连接会话行为。不要把模式切换当作无风险的即时修复;应在测试环境验证兼容性,并准备回滚配置。

数据库会话层:定位集中来源和异常占用

将会话按 application_name、用户、客户端地址和状态分组,识别是否由单个服务、任务或发布批次造成。再对照应用日志中的连接获取失败、事务超时、请求取消和重试记录,避免把重试风暴误认为单纯的连接池容量不足。

不建议批量终止所有 idle 会话:其中可能包括正常池连接,终止后应用会重新建连,短时间内反而增加连接建立压力。确需处理异常会话时,先确认所属应用、事务状态和业务影响,再针对明确目标操作。取消查询与终止会话影响不同;终止会话会使其事务中断并触发回滚,也可能导致应用重试。

系统资源层:评估提高连接上限的代价

连接上限不是越高越好。提高 PostgreSQL 连接数可能增加数据库进程及相关资源占用,具体影响取决于工作负载、配置和运行环境。变更前检查内存余量、文件描述符限制、容器或云实例资源限制,以及数据库启动参数和监控中的资源压力。

查看相关参数及是否需要重启:

SELECT name, setting, unit, context, pending_restart
FROM pg_settings
WHERE name IN (
    'max_connections',
    'superuser_reserved_connections'
);

pg_settings 可帮助确认当前生效值、配置上下文和是否待重启;保留连接相关参数会随 PostgreSQL 版本而异,应在目标版本中核对。不要把重新加载配置等同于参数已生效:需要重启的参数通常不会因配置重载而应用。

选择处置动作,并明确风险

从应用池到数据库资源的连接耗尽分层排查流程示意图

优先采用能限制新增压力、且影响范围可控的动作:

  1. 减少进入数据库的并发:对非关键任务限速或暂停,必要时对应用入口实施限流。观察请求队列、错误率和数据库会话数,避免大量请求继续排队并持续重试。
  2. 修复或回收异常占用:若已定位到连接未释放或长事务,优先修复对应应用路径;短期内确需终止特定会话时,先确认影响和回滚方式,并限制操作范围。
  3. 调整应用池或代理池:只有在确认池配置与数据库余量后再变更。降低池上限可减少数据库连接压力,但可能增加应用侧等待;提高池上限则可能把排队压力推向数据库。变更前记录原值并设置观察窗口。
  4. 评估是否提高数据库连接上限:只有在确认连接需求合理、系统资源可承受,并完成变更审批和重启计划后再考虑。修改前保存原配置;若重启后资源压力上升、连接仍持续逼近上限或错误率恶化,应按预案恢复原值并重新评估。

数据库重启不应作为默认处理手段。它可能暂时清空会话,却无法修复连接泄漏、过大的池配置或不断重试的请求;服务恢复后,连接还可能迅速再次耗尽。

恢复验证与回滚条件

处置后不要只看“错误暂时消失”。至少同时观察应用、连接池和数据库侧指标,并覆盖一个能够代表正常业务负载的观察时段。

  • 数据库连接数:总连接数不再持续逼近可用上限,且应用、任务和管理连接来源符合预期。
  • 会话状态:idle in transaction 数量和事务时长回到可解释范围;异常长时间占用不再增长。
  • 应用连接池:等待连接数、获取耗时和获取超时下降;连接池没有因过度缩小而长期排队。
  • 业务与错误指标:连接拒绝、连接获取超时和请求错误率回落,排队请求得到处理,重试没有持续放大负载。
  • 资源状况:数据库内存和系统资源保持稳定,没有因提高连接上限或集中重连出现新的资源告警。

如果变更后连接数虽下降,但应用等待和错误率上升;或数据库资源告警恶化;或连接数再次快速攀升,应停止继续扩大变更范围,按记录的原配置回滚,并回到会话来源和应用连接释放路径重新定位。

通过连接数、事务时长、连接池等待和错误率验证 PostgreSQL 恢复状态

连接数故障的关键判断顺序是:先确认排队发生在哪一层,再找出连接由谁持有、为何没有释放,最后评估是否需要调整池容量或数据库参数。将连接来源、池配置、事务时长、资源余量和回滚条件一并记录,才能避免同一问题在下一次扩容、发布或流量变化后再次出现。

发布者:jacky,转转请注明出处:https://kubiyun.com/archives/4609

赞 (0)
jacky的头像jacky
上一篇 2026-09-29 16:04
下一篇 2026-09-29

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

评论列表(8条)

  • The Silent Observer的头像
    The Silent Observer 2026-09-29 17:08

    @jacky 长事务那段太真实了,生产环境经常遇到

    • jacky的头像
      jacky 2026-09-29 17:09

      @The Silent Observer:确实,长时间 idle in transaction 很容易悄悄占住连接,还可能影响锁和数据清理。

  • 布伦希尔德英灵的头像
    布伦希尔德英灵 2026-09-29 17:31

    看到把应用池和代理池分开排查那里,很有启发

  • 星际舵手的头像
    星际舵手 2026-09-29 17:52

    确实,很多时候盲目调大 max_connections 反而会让数据库更慢

  • 细雨蒙蒙的头像
    细雨蒙蒙 2026-09-29 18:51

    应用名平时就配好,出问题时能少猜一大圈

    • 霜火双生的头像
      霜火双生 2026-09-29 19:25

      @细雨蒙蒙:对,application_name 提前配好,排查时一眼就知道连接是谁开的,省太多事了

  • 花猫朵朵的头像
    花猫朵朵 2026-09-29 18:59

    idle in transaction 真是隐形杀手,抓半天才发现是漏了回滚