生产环境出现 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 版本而异,应在目标版本中核对。不要把重新加载配置等同于参数已生效:需要重启的参数通常不会因配置重载而应用。
选择处置动作,并明确风险

优先采用能限制新增压力、且影响范围可控的动作:
- 减少进入数据库的并发:对非关键任务限速或暂停,必要时对应用入口实施限流。观察请求队列、错误率和数据库会话数,避免大量请求继续排队并持续重试。
- 修复或回收异常占用:若已定位到连接未释放或长事务,优先修复对应应用路径;短期内确需终止特定会话时,先确认影响和回滚方式,并限制操作范围。
- 调整应用池或代理池:只有在确认池配置与数据库余量后再变更。降低池上限可减少数据库连接压力,但可能增加应用侧等待;提高池上限则可能把排队压力推向数据库。变更前记录原值并设置观察窗口。
- 评估是否提高数据库连接上限:只有在确认连接需求合理、系统资源可承受,并完成变更审批和重启计划后再考虑。修改前保存原配置;若重启后资源压力上升、连接仍持续逼近上限或错误率恶化,应按预案恢复原值并重新评估。
数据库重启不应作为默认处理手段。它可能暂时清空会话,却无法修复连接泄漏、过大的池配置或不断重试的请求;服务恢复后,连接还可能迅速再次耗尽。
恢复验证与回滚条件
处置后不要只看“错误暂时消失”。至少同时观察应用、连接池和数据库侧指标,并覆盖一个能够代表正常业务负载的观察时段。
- 数据库连接数:总连接数不再持续逼近可用上限,且应用、任务和管理连接来源符合预期。
- 会话状态:
idle in transaction数量和事务时长回到可解释范围;异常长时间占用不再增长。 - 应用连接池:等待连接数、获取耗时和获取超时下降;连接池没有因过度缩小而长期排队。
- 业务与错误指标:连接拒绝、连接获取超时和请求错误率回落,排队请求得到处理,重试没有持续放大负载。
- 资源状况:数据库内存和系统资源保持稳定,没有因提高连接上限或集中重连出现新的资源告警。
如果变更后连接数虽下降,但应用等待和错误率上升;或数据库资源告警恶化;或连接数再次快速攀升,应停止继续扩大变更范围,按记录的原配置回滚,并回到会话来源和应用连接释放路径重新定位。

连接数故障的关键判断顺序是:先确认排队发生在哪一层,再找出连接由谁持有、为何没有释放,最后评估是否需要调整池容量或数据库参数。将连接来源、池配置、事务时长、资源余量和回滚条件一并记录,才能避免同一问题在下一次扩容、发布或流量变化后再次出现。
发布者:jacky,转转请注明出处:https://kubiyun.com/archives/4609
评论列表(8条)
@jacky 长事务那段太真实了,生产环境经常遇到
@The Silent Observer:确实,长时间 idle in transaction 很容易悄悄占住连接,还可能影响锁和数据清理。
看到把应用池和代理池分开排查那里,很有启发
确实,很多时候盲目调大 max_connections 反而会让数据库更慢
应用名平时就配好,出问题时能少猜一大圈
@细雨蒙蒙:对,application_name 提前配好,排查时一眼就知道连接是谁开的,省太多事了
idle in transaction 真是隐形杀手,抓半天才发现是漏了回滚
@花猫朵朵:太真实了,idle in transaction 排查起来是真的费劲,异常路径没清事务最坑