跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

ThinkPHP数据库连接超时配置:wait_timeout与interactive_timeout的参数调优

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 秒。 立即学习 “ PHP免费学习笔记(深入) ”; 实操建议: 先查现状:
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 进程还在运行、连接变量还持有句柄。所以调优不是“设大一点”,而是让应用行为和数据库策略对齐。

相关文章