MySQL server has gone away(2006错误)在Hyperf中易复现,因连接池默认不主动探测连接有效性,而MySQL服务端常设短wait_timeout(如300–600秒),协程复用超时“僵尸连接”即触发该错;需显式配置heartbeat(如30)、max_idle_time(如30)、wait_timeout(5.0)及ATTR_TIMEOUT(3)协同生效。

2006 错误不是连接断了,而是连接“被 MySQL 主动关了”后你还试图用它——Heartbeat 能提前发现并剔除这类失效连接。
为什么MySQL server has gone away在Hyperf里特别容易复现
Hyperf 的 MySQL 连接池默认不主动探测连接有效性,而 MySQL 服务端(尤其生产环境)普遍设置了较短的 wait_timeout(默认 28800 秒,但很多运维会调成 300–600 秒)。协程复用旧连接时,若该连接闲置超时,MySQL 已悄悄 CLOSE,但 Hyperf 还把它当活连接塞进查询,立刻触发 2006。
- 常见于低频请求后突然高并发:冷连接池里一堆“僵尸连接”,第一个请求就爆错
- 容器化部署更明显:MySQL Pod 重启、RDS 故障转移后,旧连接未清理,新流量打过去全挂
- 错误日志里往往只看到
PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away,没提连接来源
heartbeat配置必须显式开启且设对参数
Hyperf 的 pool.heartbeat 不是开关按钮,而是个检测策略开关。设为 -1 表示禁用;设为正整数(单位:秒)才启用心跳探活,且只对空闲连接生效。
-
heartbeat: 30:每 30 秒检查一次池中闲置连接是否还活着(发PING包) - 必须配合
max_idle_time使用:比如max_idle_time: 60,意味着连接空闲满 60 秒会被回收;那heartbeat值就得小于它(如 30),否则没机会触发检测 - 别设
heartbeat: 5:太频繁会增加 MySQL 无谓压力,也易被误判为异常波动 - 配置位置在
config/autoload/db.php的对应数据库配置下,不是全局配置
光开heartbeat还不够:三个关键协同点
单独加 heartbeat 只解决“发现失效”,不解决“避免复用失效连接”和“快速重建”。得一起调:
-
pool.wait_timeout: 5.0:协程等不到空闲连接时,最多等 5 秒就报错,别卡死——这能防止雪崩式排队 -
pool.max_idle_time: 30.0:闲置连接 30 秒内必回收,缩短“僵尸窗口期” -
options.ATTR_TIMEOUT: 3:PDO 层级执行超时设为 3 秒,避免单条慢查询拖垮整个连接(2006 常由慢查询触发连接中断)
这几项合起来,才能让连接池既及时清理“尸体”,又不因过度回收导致频繁重连。
验证heartbeat是否真起作用
不能只看日志有没有 heartbeat 字样——那只是初始化打印。要确认它在运行:
- 改小
wait_timeout到 10 秒(MySQL 端):SET GLOBAL wait_timeout = 10;,再观察应用是否在 10 秒后仍能稳定查询(而非一查就 2006) - 用
swoole_get_local_socket_count()查 worker 进程的 socket 数,开启 heartbeat 后应更平稳,不会随时间缓慢上涨 - 抓包看是否有周期性
COM_PING流量(如用tcpdump -i lo port 3306 -A),这是最直接证据
最容易被忽略的是:heartbeat 检测失败后,连接会被标记为“不可用”并立即销毁,但不会自动触发重连——重连发生在下次业务请求获取连接时。所以首次请求可能稍慢,这是设计使然,不是 bug。


















