广告广告位招商联系 ads@iiunion.comA5 · 728×90(移动端 320×100) · 参考价 ¥300/月了解详情 →

PostgreSQL 连接数打满:max_connections、连接池与空闲事务排查

Started by iiunion编辑部 · 2026-10-22 01:50 UTC · 7 views · 1 replies
I Editorial
“FATAL: sorry, too many clients already” 或 “remaining connection slots are reserved” 是 PostgreSQL 线上常见故障。很多人的第一反应是把 max_connections 调大,但这往往只是把问题推后,甚至让性能更差。本帖整理排查思路和长期方案,参数含义以官方文档为准。

一、先看当前连接都在干什么
1. 按状态统计:
SELECT state, count(*) FROM pg_stat_activity GROUP BY state;
重点关注 active、idle、idle in transaction 三类。
2. 按应用和来源统计:
SELECT usename, application_name, client_addr, count(*) FROM pg_stat_activity GROUP BY 1,2,3 ORDER BY 4 DESC;
通常能直接定位是哪个服务把连接占满。
3. 找长时间空闲事务:
SELECT pid, usename, state, now()-xact_start AS xact_age, query FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY xact_age DESC;
idle in transaction 不但占连接,还会阻止 VACUUM 回收死元组、持有锁,危害比普通 idle 大得多。

二、常见根因
1. 应用每个实例都有自己的连接池,实例数扩容后总连接数 = 实例数 × 每实例池大小,很容易超过上限。
2. 代码里开启事务后在事务中做外部调用(HTTP 请求、消息队列),或异常路径没有提交/回滚,导致 idle in transaction。
3. 连接泄漏:借出的连接没有归还,池子最终耗尽,新请求只好新建连接。
4. 慢查询或锁等待使连接长时间 active,并发请求不断堆积。

三、为什么不建议无限调大 max_connections
PostgreSQL 是每连接一个后端进程的模型,每个连接都有内存开销(work_mem 等按操作分配,并发高时总量可观),大量连接还会增加锁管理和快照计算的开销。连接数远高于 CPU 核数时,吞吐通常不升反降。更合理的做法是限制真正并发执行的数量,让其余请求在连接池里排队。

四、短期止血
1. 保留管理连接:superuser_reserved_connections 默认会为超级用户保留少量连接,确保 DBA 能登录处理。
2. 结束问题会话:确认影响后用 pg_terminate_backend(pid) 结束长时间 idle in transaction 的会话。
3. 设置超时兜底:
idle_in_transaction_session_timeout:自动终止空闲事务过久的会话;
statement_timeout:限制单条语句执行时间(建议按角色或应用分别设置,而不是全局一刀切);
较新版本还提供 idle_session_timeout,可清理长期空闲连接,但对使用连接池的应用要谨慎,避免池里的连接被服务端断开后报错。

五、长期方案:连接池分层
1. 应用侧连接池:池大小按“实际需要的并发查询数”设置,而不是按请求并发设置。一个常被引用的起点估算是“核数 × 2 + 有效磁盘数”,再结合压测调整。
2. 数据库侧连接池(如 PgBouncer):transaction 模式下多个客户端连接复用少量服务端连接,适合大量短事务的场景。注意 transaction 模式下会话级特性受限(SET 会话参数、advisory lock、部分预处理语句用法),上线前要确认应用和驱动是否兼容。
3. 按服务设置连接上限:ALTER ROLE app_x CONNECTION LIMIT n; 防止单个服务耗尽全部连接。

六、监控建议
1. 连接数按状态分类的时间曲线,idle in transaction 设单独告警;
2. 最长事务时长告警(比如超过 5 分钟);
3. 连接池侧的等待队列长度和等待时间,排队变长往往比数据库报错更早出现。

欢迎分享你们的连接池配置和踩过的坑。
I Member
补充几个生产里踩过的坑。

第一,idle in transaction 的超时建议分角色设置。idle_in_transaction_session_timeout 可以按角色分别设:给应用服务的角色设短一点(比如 5 分钟),给运维人工账号设长一些,避免 DBA 手工排查时连接被掐。statement_timeout 同理,在应用连接串或角色级别设置,不要全局一刀切,否则长报表查询会被误杀。

第二,PgBouncer 用 transaction 模式前先盘点会话级特性。prepared statement、SET/会话变量、LISTEN/NOTIFY、advisory lock 在 transaction 模式下都有坑,应用里用到这些的要先改掉或切 session 模式。升级到较新的 PgBouncer 后 prepared statement 的支持改善很多,但上线前一定要在测试环境跑全回归。

第三,监控告警按 state 分开设。pg_stat_activity 按 state 聚合成曲线,idle in transaction 单独设告警(阈值比 active 更低),同时把 xact_start 最长的前几个 pid 和 query 打进告警内容,收到告警直接能定位是哪个应用的哪段逻辑。

最后是应用侧纪律:事务里不要做 HTTP 调用、不要调消息队列,这是根因。max_connections 也不是越大越好,每连接一个后端进程,连接数远超 CPU 核数后吞吐反而下降,压测时可以用 pgbench 对比不同连接数下的 TPS 找到拐点。
广告广告位招商联系 ads@iiunion.comA6 · 728×90(移动端 320×100) · 参考价 ¥200/月了解详情 →
Log in or register to reply.