max_connections被打满主因是php-fpm进程、持久连接与mysql超时未对齐,导致“半死不活”连接堆积;pdo持久连接复用需dsn等参数完全一致,否则重复建连;php-fpm子进程数须≤mysql max_connections并预留20%余量;proxysql需手动启用连接池并正确配置;swoole协程池必须用try/finally确保归还连接。

max_connections 被打满,不是 PHP 写法有问题,而是连接生命周期没管住——PHP-FPM 进程、持久连接、数据库端超时三者没对齐,连接卡在“半死不活”状态堆满了槽位。
为什么 PDO::ATTR_PERSISTENT => true 反而让连接数暴涨
持久连接不会随脚本结束而关闭,而是被 PHP-FPM 工作进程缓存复用。但若 MySQL 的 wait_timeout(默认 28800 秒)比 PHP-FPM 进程存活时间长,连接会一直挂着;反过来,若 PHP-FPM 进程长期不重启,又没做连接状态清理,就容易积累脏连接。
- 同一组 DSN + 用户名 + 密码才能复用持久连接,换一个参数(比如多加个
charset)就新建一条,导致重复建连 - 未显式重置连接状态(如事务未提交、临时表未删、用户变量未清),下次复用时可能报错或行为异常
-
mysqli用p:localhost前缀开启持久连接,但不支持自动重连,断连后直接抛异常,无法 fallback
PHP-FPM 子进程数和 max_connections 怎么配才不撞车
每个 PHP-FPM 子进程最多持有一个持久连接(PDO 或 mysqli),所以 pm.max_children 必须 ≤ MySQL 的 max_connections,否则必然触发 “Too many connections” 错误。
- 查当前连接数:
SHOW STATUS LIKE 'Threads_connected';,再对比SHOW VARIABLES LIKE 'max_connections'; - 建议留 20% 余量:比如 MySQL 设置了
max_connections = 500,PHP-FPM 的pm.max_children不要超过 400 - 如果用了 Swoole 协程服务,
pm.max_children应设为 1(Swoole 自己管理进程),否则 FPM 和协程双层连接池会叠加占用
用 ProxySQL 做连接池代理时最常踩的坑
ProxySQL 不是装上就生效的“黑盒”,它默认不开启连接池,必须手动配置 mysql-pool_enabled 并绑定到 hostgroup。
- PHP 连接地址必须指向 ProxySQL 监听端口(如
host=127.0.0.1;port=6033),不能还连原 MySQL 端口 - ProxySQL 的
mysql-default_max_connections_per_hostgroup默认是 100,远低于实际需求,需调大(例如设为 500) - 若后端 MySQL 启用了
skip-name-resolve,ProxySQL 的mysql_servers表中hostname必须填 IP,不能填localhost,否则健康检查失败 - 连接池生效前,先确认 ProxySQL 日志里有没有类似
Pool is enabled for hostgroup的提示,没有就说明没启起来
Swoole 协程连接池里最容易漏掉的归还动作
协程连接池靠 $pool->get() 拿连接、$pool->put($conn) 归还。但很多人只记得 get,忘了 put,或者在异常分支里跳过了归还逻辑,导致连接永远卡在池里。
- 务必用
try/finally包裹:即使 SQL 执行出错、协程中断,也要确保put执行 - 不要在连接上调用
close()或disconnect(),这会销毁连接对象,池就少一个可用项 - 池初始化时的回调函数里,必须返回一个已连接成功的
Co\MySQL实例,不能只 new 不 connect,否则get()返回的是未连接对象
SHOW PROCESSLIST,看那些卡住的连接状态是 Sleep 还是 Query——前者调超时,后者查慢 SQL。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











