Hyperf MySQL连接池空闲回收依赖IdleConnectionChecker线程与max_idle_time协同工作,而非仅靠heartbeat;max_idle_time必须严格小于MySQL wait_timeout(推荐设为wait_timeout-30),heartbeat≤max_idle_time(推荐max_idle_time/2),min_connections≥2,否则易报“MySQL server has gone away”。

Hyperf 里 MySQL 连接池的空闲连接回收,不是靠客户端“自动 ping”,而是靠 IdleConnectionChecker 线程 + max_idle_time 配合 MySQL 的 wait_timeout 协同生效;配错就必然报 “MySQL server has gone away”。
max_idle_time 必须严格小于 MySQL 的 wait_timeout
这是最常踩的坑:很多人查到 MySQL 的 wait_timeout 是 60 秒,就直接把 max_idle_time 设成 60 或更大——结果连接在池里“睡过头”,取出来时已被 MySQL 主动断开。
- 查 MySQL 实际值:
SHOW VARIABLES LIKE 'wait_timeout'; -
max_idle_time推荐设为wait_timeout - 30(单位:秒),比如 MySQL 是 60,则设 30;是 28800(8 小时),则设 28770 - 这个差值留出网络延迟、线程调度、检测执行耗时等缓冲,避免临界失效
- 如果用的是 RDS 或云数据库,注意它可能有额外会话超时策略(如阿里云 RDS 的
interactive_timeout也需一并检查)
heartbeat 参数在 Hyperf 里默认不“常驻心跳”
heartbeat 开关打开 ≠ 后台持续 ping。它只在连接被取出时做一次 PING,真正负责定期探测的是 IdleConnectionChecker 线程,而它是否工作、何时工作,全看 max_idle_time 和 heartbeat 的组合。
-
heartbeat值必须 ≤max_idle_time,否则检测逻辑被跳过 - 推荐设为
max_idle_time / 2(向上取整),比如max_idle_time = 30,则heartbeat = 15 - 设太小(如 5)会导致每秒发大量
PING,压垮 DB;设太大(如 300)等于没检测 - 驱动不支持
PING(极少见)才考虑'checker' => 'SELECT 1',但该语句不能在事务中执行,否则报错
min_connections 别设 0,且要 ≥ 2
携程这类高并发、低延迟场景下,min_connections = 0 会导致冷启瞬间大量建连,触发连接风暴或超时失败。
- 服务启动后第一个请求要等完整 TCP 握手 + 认证 + 初始化,延迟陡增
- 建议设为 2~4,具体看 QPS 峰值和平均耗时:例如峰值 200 QPS、平均 DB 耗时 0.05s,理论并发连接约 10,
min_connections至少设 4 -
min_connections还影响IdleConnectionChecker的扫描范围:它只清理“空闲且超出max_idle_time”的连接,但不会把数量压到低于min_connections - Swow 引擎下必须显式配置,旧 Swoole 兼容层配置会被忽略
pool.wait_timeout 和 MySQL wait_timeout 完全是两回事
名字像,但作用对象和时机完全不同:前者是协程在池里“排队等连接”的最长容忍时间;后者是 MySQL 服务端对空闲连接的强制关闭阈值。
-
pool.wait_timeout太小(如 0.5)→ 协程等不及就抛WaitTimeoutException,日志刷屏,但池子其实有空闲连接 - 太大(如 10)→ 请求卡住很久才失败,掩盖了真实瓶颈(比如
max_connections不足、连接泄漏) - 携程级业务推荐设 2.0~5.0:既能扛住瞬时抖动,又能快速暴露问题
- 验证方式:
swoole_get_local_socket_count()或ss -s | grep ESTAB查实际 ESTABLISHED 数,长期贴近max_connections就说明池子真不够,不是超时设错了
真正难调的不是单个参数,而是这四个值的联动关系:max_idle_time、wait_timeout(MySQL)、heartbeat、min_connections。改一个不调其他,大概率引入新问题;上线前务必用压测 + 慢查询 + 连接数监控交叉验证。



















