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











