ThinkPHP 报 Connection timed out 或 MySQL server has gone away 的根源是 MySQL 的 wait_timeout 主动断开空闲连接,而非网络问题;需根据业务周期合理设置 wait_timeout(如队列任务设为300秒),并开启 break_reconnect,必要时在任务入口调用 ping() 检测重连。
ThinkPHP 报 Connection timed out 或 MySQL server has gone away,大概率不是网络不通,而是 MySQL 主动断开了空闲连接——根源就在和这两个参数没对齐业务生命周期。
为什么改了 PHP 的 connect_timeout 还是报错
很多人一看到
就去改
或 PDO 的
,但这些只控制「建立连接」那几秒的等待,而 ThinkPHP 队列任务、定时脚本、低频 CLI 命令跑着跑着崩掉,真正卡住的是「复用一个已断开的连接句柄」。MySQL 早在你上次查询后就默默把连接 kill 掉了,TP 却还当它活着。
关键判断点:
错误发生在非首次查询(比如队列 worker 启动后隔 5 分钟才执行第一条 SQL)→ 大概率是
触发
错误伴随
→ 几乎可以确定是服务端主动断连
用
查看当前连接状态,发现大量
状态且时间接近某个整数(如 300、600)→ 直接对应
值
wait_timeout 和 interactive_timeout 到底该设多少
ThinkPHP 的 CLI/队列进程属于「非交互式连接」,走的是
;而你在命令行 mysql 客户端里操作,走的是
。两者默认都是 28800 秒(8 小时),但在云数据库(如阿里云 RDS、腾讯云 CDB)上常被缩到 300 秒甚至 60 秒。
立即学习
“
PHP免费学习笔记(深入)
”;
实操建议:
先查现状:
若你的队列任务单次执行最长耗时 ≤ 30 秒,
设为
(5 分钟)足够,比盲目拉到 28800 更安全——避免连接长期空悬占用资源
不要只改
:执行
只影响后续新连接;已有连接仍按旧值计时,需重启或等其自然失效
临时调试可用
,在当前连接内生效,不影响他人
可同步设为相同值,避免命令行调试时意外断连
ThinkPHP 配置里要不要开 persistent
别急着在
里加
。PDO 持久连接在 FPM/CLI 下看似能复用 socket,但 ThinkPHP 的
类在请求结束时仍可能调用
,导致 persistent 实际失效;更麻烦的是,它不会自动检测连接是否已被 MySQL 断开。
更稳妥的做法:
显式关闭持久连接:
必须开启
,让 TP 在抛出
错误后自动重连一次(注意:仅对单条查询有效,事务中不适用)
如果用的是 Swoole 协程环境,直接绕过 TP 的 Db 类,用
或
自建连接池,此时
必须大于连接池的
(建议设为 300 秒,池内空闲连接最多活 240 秒)
ping() 不是万能的,但它是最快兜底手段
ThinkPHP 不会在每次查询前自动 ping,得你自己加。不是所有场景都适合,但对低频、长周期任务(如每 10 分钟跑一次的队列)非常有效。
写法很简单:
注意点:
是 PDO 扩展原生方法,轻量、不走 SQL 解析,失败即说明连接已断
别在每个
前都 call 一次——加在任务入口或模型初始化阶段即可
事务中禁用 ping + reconnect,否则会破坏一致性;这类场景必须靠
+ 手动超时计时兜底
最易被忽略的一点:MySQL 的
是服务端单向控制,它不管你应用层有没有在用这个连接。只要空闲时间到了,它就断——哪怕你的 PHP 进程还在运行、连接变量还持有句柄。所以调优不是“设大一点”,而是让应用行为和数据库策略对齐。
wait_timeoutinteractive_timeoutConnection timed outmysql.connect_timeoutPDO::ATTR_TIMEOUTwait_timeoutSQLSTATE[HY000] [2006] MySQL server has gone awaySHOW PROCESSLISTSleepwait_timeoutwait_timeoutinteractive_timeoutSHOW VARIABLES LIKE '%timeout%';wait_timeout300GLOBALSET GLOBAL wait_timeout = 300;SET SESSION wait_timeout = 300;interactive_timeoutdatabase.php'persistent' => trueConnectionclose()'params' => [\PDO::ATTR_PERSISTENT => false]'break_reconnect' => true2006Swoole\Coroutine\MySQLswoole/co-poolwait_timeoutmax_idle_time$connection = Db::getConnection();
if (!$connection->getPdo()->ping()) {
$connection->reconnect();
}
Db::table('user')->where('id', 1)->find();
ping()Db::table()innodb_lock_wait_timeoutwait_timeout