“too many clients”错误根源是postgresql服务端max_connections耗尽或连接未归还,需协同调优hyperf连接池max_connections(≤db侧70%)与wait_timeout(生产环境≥5.0秒),并排查idle in transaction连接。

Hyperf 3.1项目上线后QPS突增,连接池频繁触发wait timeout或报“Too many clients”,说明连接池配置与数据库侧限制已严重失配,必须立刻协同调优。
确认PostgreSQL服务端max_connections真实值
登录数据库执行:SHOW max_connections;。阿里云PolarDB for PostgreSQL按规格浮动(如4核16GB默认500),本地KES默认常为100,【不能直接套用文档默认值】。
执行SELECT count(*) FROM sys_stat_activity;核对当前活跃连接数,若接近max_connections的90%,说明服务端已濒临耗尽。
检查idle in transaction连接:SELECT pid, query, now()-state_change AS idle_duration FROM sys_stat_activity WHERE state = 'idle in transaction' ORDER BY idle_duration DESC LIMIT 5;。这类连接不释放会持续占坑,是“Too many clients”的高频诱因。
Hyperf连接池核心参数联动调优
Hyperf中max_connections和wait_timeout必须成对调整,单边修改必然引发新问题。
方法一:压测驱动下的安全上限设定
先查清DB侧max_connections值,设为N;Hyperf连接池max_connections必须≤ N × 0.7,例如DB为500,则Hyperf最大设350;【预留30%给备份、监控、DBA临时操作,不可填满】。
方法二:wait_timeout反向校准
若日志频繁出现wait timeout,说明请求排队时间过长——此时不能盲目加max_connections,而应先将wait_timeout从默认3.0秒提升至5.0~8.0秒,再观察指标;若提升后仍超时,再逐步增加max_connections,每次增幅不超过50。
方法三:空闲连接生命周期控制
设置pool.max_idle_time: 30.0(秒),避免空闲连接长期滞留占用资源;同时配pool.idle_timeout: 600(毫秒)防止连接池在低峰期过度收缩。
连接池监控与自动干预
第一步:启用HikariCP的JMX监控,在config/autoload/database.php中开启enable_jmx: true。
第二步:编写定时监控任务,每分钟采集连接池使用率:
获取activeConnections与totalConnections比值,当连续3次>0.85时,触发告警并记录堆栈;
同步检查idleConnections是否长期为0,若持续低于min_connections,说明连接未被复用或泄漏。
第三步:配合Prometheus暴露指标,重点关注hyperf_db_pool_used_connections峰值曲线,该值贴着max_connections运行即为危险信号。
多连接池场景下的读写分离避坑
为读库单独定义连接池名,如read_pool_1,命名必须全小写且不含点号,否则Db::connection('read.pool')会抛Connection [read.pool] not found。
Model查询必须显式调用on():User::on('read_pool_1')->where('status', 1)->get();with()关联查询需对每个关联模型重复指定,User::on('read_pool_1')->with(['posts' => fn($q) => $q->on('read_pool_1')])。
事务Db::transaction()只能绑定单一连接池,跨读写池事务会直接失败——从库通常禁写,INSERT语句连到read_pool_1会抛SQLSTATE[HY000]: General error: 1290 The MySQL server is running with the --read-only option。











