PHP中“数据库僵尸进程”实为MySQL服务端维持的空闲Sleep连接,由wait_timeout控制,默认8小时;PHP需主动关闭连接或设超时为60–300秒,并避免持久连接。

PHP中清理数据库连接的僵尸进程,和操作系统级的Z状态僵尸进程无关——它本质是数据库服务端维持的空闲连接,靠wait_timeout或interactive_timeout等服务端参数控制,PHP层只能配合,不能单方面“杀掉”。
为什么mysql_connect或PDO不关连接会变“僵尸”
MySQL服务端对每个客户端连接维护一个线程。当PHP脚本执行完但没显式调用mysqli_close()、$pdo = null或unset($pdo),该连接会保持打开,进入Sleep状态。若持续空闲超时(默认8小时),MySQL会主动断开;但在这之前,它仍占用服务端连接数、内存和文件描述符,表现为SHOW PROCESSLIST里一堆Sleep状态的连接——常被误称为“数据库僵尸进程”。
常见错误现象:
mysqli_connect(): (HY000/1040): Too many connections-
SHOW PROCESSLIST中大量Sleep连接,Time列值持续增长 - PHP-FPM子进程重启后,旧连接未释放,连接数只增不减
wait_timeout和interactive_timeout的区别与设置
这两个参数都控制连接空闲多久后被MySQL服务端强制关闭,但触发条件不同:
立即学习“PHP免费学习笔记(深入)”;
-
wait_timeout:适用于非交互式连接(如PHP通过mysqli或PDO建立的连接) -
interactive_timeout:仅对设置了CLIENT_INTERACTIVE标志的连接生效(如MySQL CLI客户端)
PHP默认不设该标志,所以真正起作用的是wait_timeout。生产环境建议设为60~300秒(1~5分钟),而非默认的28800(8小时)。
修改方式(需有SUPER权限):
SET GLOBAL wait_timeout = 300; SET GLOBAL interactive_timeout = 300;
注意:SET GLOBAL只影响新建立的连接;已存在的Sleep连接仍按原值计时。持久化需写入my.cnf:
[mysqld] wait_timeout = 300 interactive_timeout = 300
PHP代码层必须做的三件事
服务端参数只是兜底,PHP自身必须主动管理连接生命周期:
- 避免在长生命周期脚本(如CLI守护进程、Swoole Worker)中复用全局
$pdo而不检查有效性;每次使用前加$pdo->ping(),失败则重建 - 在
try/catch或finally块中显式关闭:用mysqli_close($conn),或置$pdo = null(PDO对象析构时会自动close,但依赖GC时机) - 禁用持久连接:
PDO::ATTR_PERSISTENT => false(默认即false);若误启了mysql.allow_persistent或PDO::ATTR_PERSISTENT,会导致连接无法被脚本结束释放
如何验证连接是否真被释放
别只看PHP变量是否unset——要查MySQL服务端实际状态:
- 登录MySQL,执行
SHOW PROCESSLIST,筛选Command = 'Sleep'且Time > 300的连接 - 观察
Threads_connected变量变化:SHOW STATUS LIKE 'Threads_connected'; - 在PHP中启用
mysqli.report(MYSQLI_REPORT_ALL),捕获连接泄漏类警告
最易被忽略的一点:Swoole协程MySQL客户端(如swoole_mysql或co\MySQL)不走传统MySQL协议栈,其连接池行为由max_idle_time和max_active控制,和wait_timeout完全无关——那是另一套机制。



















