核心是协程复用错误、mysql服务端wait_timeout断连、客户端未校验三者叠加;修复须每次query前ping校验,失败则新建连接,归还时不close,禁用break_reconnect与coroutine::close,并设pop超时500ms。

ThinkPHP 6 在 Swoole 环境下报 MySQL server has gone away,核心不是“Swoole 导致连接失效”,而是协程模型下连接复用逻辑与 MySQL 服务端超时机制冲突所致。关键问题在于:连接被多个协程错误共享、空闲期间被 MySQL 主动断开(wait_timeout)、客户端未做状态校验就直接复用。
必须每次 query 前 ping 校验连接有效性
别信 $pdo->connected 或 getAttribute(PDO::ATTR_CONNECTION_STATUS),它们不反映真实 TCP 连接状态。唯一可靠方式是主动 ping:
- 在业务查询前调用
Db::getPdo()->ping(),失败则关闭并重建连接 - 更稳妥做法是 try-catch 查询,捕获
2006错误后手动重连再执行 - 注意:不能对已失效的 PDO 实例调用
connect(),应Db::close()后再Db::connect()
禁用 break_reconnect,改用显式保活逻辑
Swoole 常驻进程下,TP 原生的 break_reconnect 基本无效——它只在查询失败后触发一次重连,无法应对连接在空闲期就被 MySQL 断开的问题:
- 配置中设
'break_reconnect' => false,避免干扰协程生命周期 - 在任务循环开头加轻量探测:
Db::query('SELECT 1'),失败则Db::clear()并重新初始化 - 探测频率按业务节奏来,例如每 5–10 分钟一次;切勿高频(如每秒)调用
规避连接复用陷阱与资源泄漏
协程间错误复用同一个 PDO 实例,或归还时不清理,会导致连接堆积、time_wait 堆积甚至 fd 泄漏:
- 禁止全局变量或静态属性缓存
PDO实例,每个协程应独立管理连接生命周期 - 使用连接池时,pop 超时务必设为
500(毫秒),避免协程卡死在阻塞等待 - 归还连接时不要调用
close(),而是push()回池;若 ping 失败,则丢弃该实例,新建连接 - 禁用
Coroutine::close(),防止协程销毁时异常释放 fd
同步调整 MySQL 服务端参数
仅改应用层不够,需配合服务端设置,避免过度宽松导致连接堆积:
- 将
wait_timeout和interactive_timeout设为300–600(5–10 分钟),与应用层心跳节奏匹配 - 确认
max_allowed_packet足够大(如64M),并确保 PHP 客户端也同步设置(PDO 参数中加PDO::MYSQL_ATTR_MAX_PACKET_SIZE) - 检查
Threads_connected是否接近max_connections,若真耗尽需扩容或优化连接复用
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











