首页 > Technology > 正文

PostgreSQL 连接数满了怎么办?too many clients 的应急与根治

fenij 2026-08-07 3 Technology

应用突然大面积报错,日志里一行 FATAL: sorry, too many clients already。这时候数据库还活着,只是不再接受新连接了。先别急着改 max_connections 重启——那样多半几小时后又会满。下面按「先止血、再定位、最后根治」的顺序来。

三分钟应急:把连接腾出来

PostgreSQL 默认给超级用户留了 3 个连接槽(superuser_reserved_connections),所以用 postgres 账号一般还能进去:

psql -U postgres -c "SELECT count(*), state FROM pg_stat_activity GROUP BY state;"

大概率会看到一堆 idleidle in transaction。后者最危险,它不仅占连接,还卡住 vacuum。先把闲置超过 10 分钟的事务砍掉:

SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
  AND state_change < now() - interval '10 minutes'
  AND pid <> pg_backend_pid();

业务通常能立刻恢复。但这只是止血。

看清楚连接是被谁占的

state 说明 怎么处理
active 正在执行 SQL 看 query 字段,慢 SQL 优先优化
idle 连着但没干活 连接池配置过大,调小 max pool size
idle in transaction 开了事务忘了提交 应用代码漏 commit/rollback,必须改
idle in transaction (aborted) 事务已报错未回滚 异常处理里补 rollback

想知道是哪个服务在占,按客户端地址和应用名分组:

SELECT client_addr, application_name, count(*)
FROM pg_stat_activity
GROUP BY 1,2 ORDER BY 3 DESC;

我见过最典型的一次,是一个定时脚本每分钟建连接不关,跑了两天把 200 个槽吃光。

max_connections 到底该设多大

很多人第一反应是把 100 改成 1000。PostgreSQL 是进程模型,每个连接一个后端进程,连接数上去内存和上下文切换都会涨。经验值是按 CPU 核数和可用内存算,别盲目堆。

🧮
100
默认 max_connections
💾
5-10MB
单连接基础内存开销
⚙️
3
超级用户预留槽位

改配置后需要重启才生效:

ALTER SYSTEM SET max_connections = 300;
-- 重启服务
sudo systemctl restart postgresql

顺带把这两项也设上,能自动清掉忘记提交的事务:

ALTER SYSTEM SET idle_in_transaction_session_timeout = '5min';
ALTER SYSTEM SET statement_timeout = '30s';
SELECT pg_reload_conf();

根治靠连接池,不靠调大参数

业务侧有多少个实例、每个实例连接池上限多少,乘起来就是数据库要扛的连接数。四个应用实例各配 50,就是 200 个常驻连接,而它们大部分时间是 idle。正确做法是在数据库前面放 PgBouncer,用 transaction 模式复用:

[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25

前端可以挂 1000 个客户端连接,真正落到 PostgreSQL 的只有 25 个。应用侧连接池同时调小到 10 以内,两边配合才有效果。注意 transaction 模式下不能用会话级特性,比如 SET 变量和预备语句要按文档处理。

常见问题

问:pg_terminate_backend 会不会丢数据?
答:正在执行的事务会回滚,未提交的数据丢失,已提交的不受影响。所以优先杀 idle in transaction,别一上来就把 active 全清了。

问:max_connections 改了没生效?
答:这个参数是 postmaster 级别的,pg_reload_conf() 不管用,必须重启实例。用 SHOW max_connections; 确认当前值。

问:连接池已经很小了,还是满?
答:多半有长事务在积压。查 SELECT pid, now()-xact_start AS dur, query FROM pg_stat_activity WHERE state <> 'idle' ORDER BY dur DESC LIMIT 10;,把跑最久的那几条 SQL 拿去优化。

你们线上 max_connections 和连接池是怎么配的?欢迎到 fenij.com 留言交流具体数值。