快速定位php中未关闭的mysql连接,需结合show processlist查sleep超时连接、在连接创建前记录调用栈日志、检查cli脚本与持久化配置,并确保异常分支中显式关闭或置null释放。

MySQL报 Too many connections 错误,PHP 里怎么快速定位是谁开的连接?
不是所有连接都来自当前请求——PHP-FPM 进程可能复用、PDO 可能没关、长连接可能滞留。先别急着调大 max_connections。
- 查活跃连接:
SHOW PROCESSLIST;,重点关注Command列为Sleep且Time超过 30 秒的,大概率是 PHP 没显式关闭的连接 - 加日志:在每次
new PDO()或mysqli_connect()前记录debug_backtrace()的前两层函数名和文件行号,用error_log()输出到独立文件 - 注意 CLI 脚本:定时任务里的 PHP 脚本如果用
pconnect或忘了$pdo = null,连接会一直挂在 MySQL 上,比 Web 请求更难发现
PHP 用 PDO 还是 mysqli?连接要不要设 PDO::ATTR_PERSISTENT?
持久连接不是“省资源”,而是把连接生命周期从请求级拉长到进程级,用错反而加剧连接堆积。
-
PDO和mysqli在连接管理行为上几乎一致,选哪个主要看团队习惯;但PDO默认不开启持久化,mysqli的mysqli_pconnect()是显式 API,更不容易误用 -
PDO::ATTR_PERSISTENT => true只在 PHP-FPM 的 static 或 ondemand 模式下有意义;若用 prefork 模式(比如 Apache + mod_php),每个子进程会维护自己的持久连接池,极易撑爆max_connections - 真实高并发场景下,更推荐关掉持久化,靠连接池中间件(如 ProxySQL)或应用层连接复用(如 Laravel 的
DB::connection()->reconnect())来控量
PHP-FPM 配置里哪些参数直接影响 MySQL 连接数?
MySQL 连接数上限不是孤立指标,它被 PHP-FPM 的进程模型死死卡着。
-
pm.max_children是硬上限:每个 child 最多开一个 MySQL 连接(非持久化时),所以max_connections至少要比它大 20% 才有余量 -
pm.start_servers和pm.min_spare_servers决定空闲连接数基线——这些进程一启动就可能建连,尤其用了mysql.default_host这类自动连接配置时 - 别忽略
request_terminate_timeout:超时后 PHP 强制终止,但 MySQL 连接未必立刻释放,表现为SHOW PROCESSLIST里一堆sleep状态卡 60 秒以上
为什么加了 mysqli_close() 还是连不上?
不是所有“关闭”都真断开了网络连接,尤其用对象方式操作时,行为差异很隐蔽。
立即学习“PHP免费学习笔记(深入)”;
- 用
mysqli_close($conn)显式关闭有效;但写成$conn->close()后又对$conn做了任何访问(比如var_dump($conn)),会触发 PHP 内部重建连接 - PDO 对象设为
null($pdo = null;)才真正释放;只unset($pdo)不够,因为引用计数可能没清零 - 最常漏的是异常分支:
try/catch里连接成功了,但后续逻辑抛异常跳出了作用域,finally没写关闭逻辑,连接就挂在那里了
连接数问题从来不是单点故障,是 PHP 生命周期、MySQL 状态、FPM 模型三者咬合出来的结果。盯住 SHOW PROCESSLIST 里的 Time 字段,比调参数管用得多。











