Hyperf 连接池不会自动重建 MySQL 重启后的失效连接,因默认不主动探测状态;需配置 heartbeat、max_idle_time(须小于 MySQL wait_timeout)协同触发空闲连接剔除与重建。

Hyperf 连接池不会在 MySQL 服务重启后自动重建“失效连接”,而是继续复用已断开的句柄,直接触发 PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away。根本原因不是连接池不重建,而是它默认不主动探测连接状态——必须靠 heartbeat + max_idle_time + MySQL wait_timeout 三者协同,才能让旧连接被及时剔除、新连接按需重建。
为什么MySQL重启后第一个请求必报2006
MySQL 服务重启时,所有已有 TCP 连接被内核强制关闭,但 Hyperf 连接池里的 PDO 对象仍持有无效句柄;协程取出连接时不校验有效性,直接发查询,MySQL 返回 RST 包,PDO 就抛出 2006。这不是网络问题,是连接池“僵尸连接”未清理。
- 现象:MySQL Pod 重启 / RDS 故障转移后,首条请求立刻失败,后续请求可能成功(因部分连接已被踢出或重连)
- 关键点:
heartbeat默认不生效——它只在连接被取出时单次调用PING,不是后台常驻检测 - 真正起作用的是空闲连接检查线程
IdleConnectionChecker,它只对idle状态连接做心跳,且依赖max_idle_time触发回收
heartbeat 和 max_idle_time 必须严格配对
单独设 'heartbeat' => 30 没用,max_idle_time 必须小于 MySQL 的 wait_timeout,否则连接在池里“睡过头”,取出来时已经失效。
- 先查 MySQL 实际值:
SHOW VARIABLES LIKE 'wait_timeout';(生产常见 300 或 600 秒) -
max_idle_time推荐设为wait_timeout - 30(留缓冲,避免边界竞争) -
heartbeat必须 ≤max_idle_time,推荐设为max_idle_time / 2(如max_idle_time=270,则heartbeat=135) - 若
max_idle_time > wait_timeout(比如 MySQL 是 60s,你设了 120s),那所有 idle 连接都会活过期,取出来就炸
配置位置和最小必要参数
这些参数必须写在 config/autoload/db.php 的具体数据库配置下,不是全局配置;漏掉任一关键项,心跳机制就形同虚设。
'pool' => [ 'min_connections' => 2, // 别设 0,冷启延迟高'max_connections' => 50,'connect_timeout' => 5.0, // 网络抖动时比默认 10.0 更稳'wait_timeout' => 5.0, // 协程等连接的上限,防排队雪崩'max_idle_time' => 270.0, // 必须'heartbeat' => 135, // 后台每 135 秒扫一次 idle 连接]- 还要加:
'options' => [ \PDO::ATTR_TIMEOUT => 3 ] // 防慢查询拖垮连接
验证心跳是否真在工作
别只看日志里有没有 “heartbeat initialized”——那是初始化打印,不代表它在运行。真实验证方式只有两个:
- 手动让 MySQL 重启(或 kill -9 mysqld 进程),然后立刻发请求:如果 2006 消失、请求正常返回,说明配置生效
- 查连接池当前 idle 连接数:
DB::connection()->getPool()->getIdleConnectionCount(),等待超过heartbeat时间后,再查一次,应有波动(说明检查线程在活动) - 注意:
checker默认用PING,不要改成SELECT 1——它走完整 SQL 流程,开销大 3–5 倍,且事务中会失败
最容易被忽略的是 max_idle_time 和 MySQL wait_timeout 的大小关系——配反了,心跳就变成定时炸弹,既不剔除僵尸连接,又制造大量无效 PING。



















