Hyperf的pool.heartbeat非即开即用,需与max_idle_time、MySQL wait_timeout协同配置:max_idle_time必须严格小于wait_timeout,heartbeat值应介于二者之间(如wait_timeout=300s,则max_idle_time=270s、heartbeat=135s),否则将导致连接失效或无效探测。

Hyperf 的 pool.heartbeat 不是“开就灵”的开关,它默认不生效,且和 MySQL 的 wait_timeout、连接池空闲策略强耦合——配错反而增加数据库压力或掩盖真实断连。
为什么开了 pool.heartbeat 还是报 “MySQL server has gone away”
根本原因不是没心跳,而是心跳时机/方式不对:
-
pool.heartbeat默认值为false,即使你在配置里写了'heartbeat' => true,Hyperf 也不会自动启用(它只在连接被取出时做一次PING,不是后台常驻探测) - 真正起作用的是连接池的后台空闲检查线程(
IdleConnectionChecker),它依赖max_idle_time和heartbeat两个参数协同工作 - 如果
max_idle_time> MySQL 的wait_timeout(比如 MySQL 是 60s,你设了 120s),那连接在池里“睡过头”,取出来时必然已失效 -
heartbeat设太小(如 5s)会导致每秒大量PING,压垮数据库;设太大(如 300s)等于没检测
pool.heartbeat 和 max_idle_time 怎么配才不冲突
必须按 MySQL 服务端参数反推:
- 先查 MySQL 实际的
wait_timeout:SHOW VARIABLES LIKE 'wait_timeout';(通常 60–28800,生产常见 300 或 600) -
max_idle_time必须严格 wait_timeout,建议设为wait_timeout - 30(留 30 秒缓冲) -
heartbeat值应 ≤max_idle_time,推荐设为max_idle_time / 2(例如max_idle_time=270,则heartbeat=135) -
min_connections别设 0,否则空闲时全关,冷启延迟高;建议 ≥ 2
示例(MySQL wait_timeout=300):
'pool' => [
'min_connections' => 2,
'max_connections' => 32,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => 135, // ✅ 后台每 135s 检查一次空闲连接
'max_idle_time' => 270, // ✅ 空闲超 270s 就 close,早于 MySQL 的 300s
],
心跳检测用 PING 还是 SELECT 1
Hyperf 默认用 PING,但前提是驱动支持。MySQLi 和 PDO_MySQL 都支持,无需改;但如果你用了自定义封装或老版本驱动:
- 确认
Db::connection()->getPdo()调用ping()不抛异常(可写个简单命令验证) - 不要手动改成
SELECT 1:它走完整查询流程,有 SQL 解析、权限检查开销,比PING慢 3–5 倍 - 若驱动不支持
PING(极少见),再退而求其次配'checker' => 'SELECT 1',但必须确保该语句在所有环境下都合法(比如不能在事务中执行)
连接断开后重试逻辑不能只靠心跳
心跳只能清理“睡死”的连接,无法处理运行中突然断开(如网络抖动、MySQL 重启):
- 必须配合异常捕获 + 重连:监听
Hyperf\Database\Events\StatementPrepared或直接 try/catchPDOException - 重点识别错误码
2006、错误信息含server has gone away或Connection refused - 重试前调用
$this->db->reconnect()(Hyperf 提供),而不是close() + connect()手动重建 - 避免在事务中触发重连——会隐式提交,破坏一致性
最容易被忽略的一点:连接池的 heartbeat 是后台异步行为,它不会阻塞业务请求;但你的重试逻辑是同步的,这两者要分清职责——前者保池子健康,后者保单次查询可用。











