根本原因是PHP持久连接未关闭与MySQL max_connections设置过高叠加导致连接耗尽;需禁用非框架脚本的持久化、合理调低max_connections及wait_timeout,并修复代码中漏掉的mysqli_close()。

不是连接池“溢出”,而是 PHP 持久连接没关 + MySQL max_connections 设太高,两者叠加把连接槽位占死。
PHP 的 pdo_mysql 或 mysqli 持久连接会复用底层 socket,但不自动释放
宝塔默认启用 mysql.allow_persistent = On(PHP 配置),而大多数 PHP 脚本根本没调用 mysqli_close() 或置空 $pdo。结果是:每个请求结束,连接没断,只是回到连接池“待命”;并发一高,Threads_connected 就卡在高位不降。
- 检查当前持久连接是否真被复用:执行
SHOW PROCESSLIST;,看大量连接的Command是Sleep且Time持续增长(比如 > 60 秒) - 临时禁用持久化:在
/www/server/php/{版本}/etc/php.ini中设mysql.allow_persistent = Off、mysqli.allow_persistent = Off,然后bt reload重载 PHP - 不建议全局关——若用了 Laravel / ThinkPHP 等框架,它们内部已管理连接生命周期,此时关反而降低性能;重点查自写脚本或老项目里的裸
mysqli_connect()
MySQL 的 max_connections 不能只看“够不够”,要看“谁在占”
设成 500 看似宽裕,但如果应用层漏关连接,10 个并发就能累积 100+ Sleep 连接,再加定时任务、后台脚本、phpMyAdmin 页面挂着,很快打满。关键不是上限,是连接存活时间。
- 先确认真实压力:执行
SHOW STATUS LIKE 'Threads_connected';和SHOW STATUS LIKE 'Threads_created';,如果后者数值持续上涨(每秒新增 > 1),说明连接频繁重建,大概率是没复用或没关 - 宝塔里改配置必须写进
[mysqld]段:路径是/www/server/mysql/etc/my.cnf,加一行max_connections = 120(中小站点够用),别写在文件开头或[client]下 - 别信“调大就安全”——
max_connections每增加 1,MySQL 就多分配一份线程栈内存(默认 256K),1000 连接 ≈ 256MB 额外开销,容易触发 OOM
wait_timeout 和 interactive_timeout 是控制“僵尸连接”的实际开关
这两个参数决定空闲连接多久后被 MySQL 主动断开。默认 28800 秒(8 小时),对 Web 场景完全不合理——用户页面关了,PHP 进程却还握着连接不放。
立即学习“PHP免费学习笔记(深入)”;
- 在
my.cnf的[mysqld]段下加:wait_timeout = 60、interactive_timeout = 60(单位秒) - 注意:这个值不能低于 PHP 的
max_execution_time,否则长查询中途被断连;一般设为 60–180 即可 - 改完重启 MySQL 生效,之后再查
SHOW VARIABLES LIKE '%timeout%';确认是否加载成功
真正卡住的从来不是配置数字,而是代码里那句漏掉的 mysqli_close($conn),或者框架配置里没开连接回收。调参只是兜底,定位并修复连接泄漏点才是根治办法。











