“Too many connections”或“Too many clients”本质是连接未释放或池与DB限制错配;需三步处理:先查真实连接数(MySQL用SHOW VARIABLES/STATUS,PostgreSQL用SHOW max_connections和pg_stat_activity),再定位僵尸连接(MySQL盯Sleep>300s,PostgreSQL查idle in transaction),最后协同调优Hyperf max_connections(≤DB侧70%)与wait_timeout(≥5.0秒),并堵住代码泄漏。

Hyperf 多库连接报 “Too many connections”(MySQL)或 “Too many clients”(PostgreSQL),本质不是“连多了”,而是连接资源被长期占用、未释放,或连接池与数据库侧限制严重错配。修复要分三步走:先确认是不是真满了,再定位谁在占着不放,最后调优配置+堵住泄漏点。
一、别急着改配置,先确认是不是真连满了
用管理员账号 socket 直连数据库(哪怕应用连不上),执行:
-
MySQL:
SHOW VARIABLES LIKE 'max_connections';查上限(默认 151);SHOW STATUS LIKE 'Threads_connected';看当前连接数;SHOW STATUS LIKE 'Max_used_connections';看历史峰值——长期 >90% 就该干预 -
PostgreSQL:
SHOW max_connections;查服务端上限(阿里云 PolarDB 按规格浮动,如 4 核 16GB 是 500);SELECT count(*) FROM pg_stat_activity;看当前活跃连接;重点查idle in transaction连接:SELECT pid, query, now()-state_change AS idle_duration FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY idle_duration DESC LIMIT 5;
注意:MySQL 允许 max_connections + 1 个连接,那多出的 1 个是留给 SUPER 权限用户的,所以你还能登进去,靠的就是它。
二、查清楚谁在卡着连接不放
不是所有连接都该杀,重点盯 Sleep 或 idle 超时的“僵尸连接”:
- MySQL 中查空闲太久的连接:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE FROM information_schema.PROCESSLIST WHERE COMMAND = 'Sleep' AND TIME > 300;(>5 分钟才考虑处理) - 按用户/IP 归类看异常来源:
SELECT USER, HOST, COUNT(*) FROM information_schema.PROCESSLIST GROUP BY USER, HOST ORDER BY COUNT(*) DESC;快速定位是哪个服务、哪台机器在泄漏 - PostgreSQL 中重点关注
state = 'idle in transaction'的连接,这类事务没提交/回滚,连接一直挂着,是高频诱因
三、协同调优连接池与数据库限制
Hyperf 的 max_connections 不是越大越好,必须和数据库侧限制匹配:
- Hyperf 连接池的
max_connections建议 ≤ 数据库max_connections的 70%,留余量给备份、监控、DBA 临时操作 -
wait_timeout生产环境建议设为 5.0 秒(MySQL)或 ≥5.0(PostgreSQL),太小会频繁报 wait timeout,太大则掩盖真实瓶颈 - 多库场景下,每个库的连接池都要单独评估:比如 A 库峰值 QPS×平均耗时≈12,B 库≈8,则两库
max_connections分别设 30 和 20 比设统一 100 更安全 - 检查系统级限制:
ulimit -n是否足够;systemd 管理的 MySQL/PgSQL 需在 service 文件里加LimitNOFILE=65536并 reload
四、堵住代码层连接泄漏
90% 的问题根源在应用没释放连接:
- Hyperf 中使用
$this->db->connection()后,务必确保在finally块中显式释放,或用try-with-resources类写法(PHP 无原生支持,需手动 wrap) - 避免在事务中调用 HTTP/RPC/第三方接口,或做耗时操作(如
sleep(10)),否则连接会长期被锁住 - Redis pipeline / MySQL transaction 未
exec()或未commit()/rollback(),会导致连接被协程独占直到退出 - PHP 脚本发生 fatal error 时,
mysqlnd不会自动归还连接,必须显式mysqli_close()或依赖脚本结束——不可靠,应主动管理


















