该错误是MySQL主动断开空闲连接所致,需从数据库(查wait_timeout、Sleep连接、Threads_connected)、配置(TP8需启用break_reconnect且PDO异常模式开启)、运行环境(FPM/CLI/长驻进程差异化处理)三层排查,并通过SELECT 1心跳探测和日志验证重连实效。
![thinkphp8连接数据库报sqlstate[hy000] [2006] mysql server has gone away怎么排查](https://img.php.cn/upload/article/001/503/042/179047832752947.png)
这个错误不是 ThinkPHP 8 的 bug,而是 MySQL 主动断开空闲连接导致的。排查要从数据库、配置、运行环境三层入手,重点看连接是否真断了、框架有没有重连能力、代码是否在错误时机复用了失效连接。
查 MySQL 是否真断开了连接
登录数据库执行:
- SELECT id, user, host, db, time, info FROM information_schema.processlist WHERE command = 'Sleep' AND time > 30; —— 看是否有大量空闲超 30 秒的连接;time 越大越可疑
- SHOW STATUS LIKE 'Threads_connected'; 和 SHOW VARIABLES LIKE 'max_connections'; —— 如果前者持续接近后者(如 145/150),说明连接数耗尽;如果只有 20 却频繁报错,大概率是“假性断连”,即连接被 MySQL 清理但 PHP 还在用
- SHOW VARIABLES LIKE 'wait_timeout'; —— 默认常为 28800 秒,但云环境或容器中可能被设成 60~300,这是关键诱因
核对 ThinkPHP 8 数据库配置是否生效
确保 config/database.php 中 MySQL 配置块满足以下全部条件:
-
type 必须是 'pdo_mysql' —— 用 mysqli 或旧驱动时,
break_reconnect完全不生效 -
params 数组里必须包含
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION—— 否则 PDO 不抛异常,TP 收不到错误信号,重连逻辑根本不会触发 -
显式开启
'break_reconnect' => true—— 默认是 false,不写等于没配 - 容器或 Docker 环境下,hostname 不能写 'localhost' 或 '127.0.0.1',必须填数据库服务名(如 'mysql');port 必须是整数 3306,不能是字符串 '3306'
区分运行场景,针对性处理
不同入口行为差异很大,不能一套配置打天下:
立即学习“PHP免费学习笔记(深入)”;
-
FPM Web 请求:可全局启用
break_reconnect,但要注意事务中它会失效——事务中途断连后重连,原事务上下文已丢失,rollback 不起作用 -
CLI 队列任务(如 queue:work):不要全局开
break_reconnect。应在命令类execute()开头动态设置:\think\facade\Config::set('database.connections.mysql.break_reconnect', true);,确保只影响当前进程 -
work 模式长驻进程:仅靠
break_reconnect不够,它只在查询失败后重连一次。需在任务循环开头加心跳探测:\think\Db::query('SELECT 1');,捕获异常后调用\think\Db::clear();。频率按业务节奏来,比如每 5–10 分钟一次即可
验证连接状态与日志输出
光配对没用,得确认它真在工作:
- 打开
app_debug = true或配置日志等级,让 TP 输出重连动作。没看到日志不等于没重连,可能是 debug 关了或日志路径不对 - 别依赖
Db::getPdo()->getAttribute(PDO::ATTR_CONNECTION_STATUS)判断连接存活——部分 PDO 版本返回不可靠。更稳妥的是直接执行一条简单查询并 try-catch,捕获2006错误码再处理 - 检查 runtime/log/ 下最近日志,搜索
went away、1040、refused,高频出现gone away基本指向 wait_timeout 过短或心跳缺失



















