ThinkPHP 使用 PDO 连接 MySQL 时,mysqlnd 不主动检测连接超时,导致出现“MySQL server has gone away”而非连接异常;需手动执行 SELECT 1 探测并重连,禁用持久连接,CLI/Swoole 场景须主动管理连接生命周期。
连接超时后
不抛异常,
仍认为连接有效
ThinkPHP 默认用 PDO 连接 MySQL,但底层驱动(如
)在连接断开后不主动检测,导致后续查询报
而不是连接失败。框架不会自动重连,而是直接抛出异常或返回空结果。
常见错误现象:
触发场景:MySQL 的
或
触发(默认 8 小时),但更常见于长任务、队列进程、CLI 模式下空闲连接
关键点:
的
不做心跳检测,
前也不会 ping 连接
解决思路:必须在执行前手动检查连接有效性,不能依赖框架自动恢复
配置里加
没用
这个配置项只在「查询执行过程中发生连接中断」时起作用,且仅对部分驱动(如
)有效;
驱动下基本不生效。它不是“连接前自动重连”,而是“执行失败后尝试重连一次再执行”,但多数情况下失败已不可逆。
参数差异:
与
(读写分离)组合时逻辑更复杂,容易误判重连时机
性能影响:每次查询都可能多一次失败重试,增加响应延迟,且无法避免首次失败的业务异常
真实效果:遇到
时大概率仍抛异常,而不是静默恢复
手动 ping + 重连的最小可行方案
最可靠的做法是在关键查询前调用
或直接
—— 但 PDO 本身没
方法,得退回到底层连接对象。
推荐做法:用
做轻量探测(比
更准)
封装建议:在模型基类或中间件中统一拦截,例如重写
,加一层连接保活逻辑
示例片段:
注意:不要在事务中做这个探测,否则可能破坏事务一致性
CLI 进程和长连接场景必须主动管理生命周期
Web 请求天然短生命周期,连接用完即丢;但 CLI(如队列消费者、定时任务)会复用连接数小时,这时超时几乎必然发生。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
立即学习
“
PHP免费学习笔记(深入)
”;
必须做:启动时记录连接创建时间,在每次查询前检查是否超过
秒
不能依赖:
或
关闭连接,它们不一定及时执行
兼容性提醒:PHP 8.1+ 的
会加剧该问题,持久连接更难被回收,务必禁用
一个容易被忽略的点:Swoole 或 Hyperf 等常驻内存框架里,
实例是单例,连接状态跨请求共享,必须自己维护连接健康度
事情说清了就结束。
mysqlndthinkphpmysqlndMySQL server has gone awaySQLSTATE[HY000]: General error: 2006 MySQL server has gone awaywait_timeoutinteractive_timeoutthinkphpDb::connect()->query()Db::connect()'break_reconnect' => truemysqliPDO'break_reconnect' => true'deploy' => 1server has gone awayDb::getPdo()->getAttribute(PDO::ATTR_CONNECTION_STATUS)ping()ping()Db::getPdo()->exec('SELECT 1')getAttributeBaseModel::getConnection()try {
$pdo = Db::getPdo();
$pdo->exec('SELECT 1');
} catch (\PDOException $e) {
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
Db::close(); // 强制关闭旧连接
Db::connect(); // 触发新连接
}
}wait_timeout - 30__destructregister_shutdown_functionPDO::ATTR_PERSISTENTDb