该错误非tp的bug,而是mysql主动断开空闲连接所致;启用break_reconnect需配对pdo::attr_errmode=>pdo::errmode_exception才生效,否则不触发重连。
![thinkphp数据库配置实战:解决“sqlstate[hy000] [2006] mysql server has gone away”的超时重连机制](https://img.php.cn/upload/article/001/503/042/178078497841149.png?x-oss-process=image/resize,p_40)
直接说结论:这个错误不是 TP 的 bug,而是 MySQL 主动断开空闲连接导致的;启用 break_reconnect 是最常用解法,但必须配对 PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,否则它根本不会触发。
为什么 break_reconnect 设了 true 却没用?
常见错误现象是配置写了、重启了、日志里还是直接抛出 SQLSTATE[HY000] [2006] MySQL server has gone away,没看到重连动作。根本原因往往就两个:
-
type不是pdo_mysql—— 用了mysqli或旧版mysql驱动,break_reconnect完全不生效 -
params里没设PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION—— PDO 默认是静默模式(PDO::ERRMODE_SILENT),异常不抛出,框架根本收不到错误信号,自然不会走重连逻辑 - 没开
debug或没配日志,重连成功了你也看不到输出,误以为失效
CLI 队列任务要不要全局开 break_reconnect?
不要。FPM Web 请求和 CLI 队列共用一套数据库配置时,全局开启 break_reconnect 在事务中可能引发数据不一致 —— 比如事务中途连接断开,重连后继续执行,但原事务上下文已丢失,rollback 失效。
更安全的做法是只在 CLI 入口动态启用:
- 在命令类的
execute()开头调用config('database.connections.mysql.break_reconnect', true) - 或手动重初始化连接:
\think\Db::init(array_merge(config('database.connections.mysql'), ['break_reconnect' => true])) - 确保该配置变更仅作用于当前进程生命周期,不影响 Web 请求链路
work 模式队列下,光靠 break_reconnect 还不够
ThinkPHP-Queue 的 work 模式是常驻进程,一次连接复用到底。而 break_reconnect 只在「查询失败后」才触发一次重连,它不解决连接空闲期间被 MySQL 单方面断开的问题 —— 下一个查询来之前,连接可能已经死了。
此时需要主动心跳探测:
- 在任务循环体开头加
Db::query('SELECT 1'),捕获异常后执行Db::clear() - 不要用
SELECT 1做高频探测(比如每秒一次),按业务节奏来,例如每 5–10 分钟一次即可 - 注意:此方式不适用于事务中,因为
SELECT 1会开启隐式事务,干扰业务逻辑
连接池、Swoole 环境下 break_reconnect 基本失效
TP 原生不支持连接池,think-swoole 扩展也只封装协程 MySQL 客户端,并未接管连接生命周期。你在 Swoole Server 中用 Db::table() 发起的查询,仍走 TP 的 PDO 连接管理,但长连接 + 协程调度会让 break_reconnect 的检测时机错乱。
真要上连接池,得绕过 TP 的 Db 类:
- 用
Swoole\Coroutine\MySQL自建协程池,maxIdleTime必须小于 MySQL 的wait_timeout - 池中连接归还前,必须执行
$conn->query('SELECT 1')或检查$conn->connected - TP 的模型、查询构造器完全无法接入这套机制,只能手写原生 SQL
复杂点在于:你得自己维护连接健康状态,而不是依赖框架那套“报错再重连”的被动逻辑 —— 主动探测和连接回收时机,才是长连接场景下真正容易被忽略的环节。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











