max_idle_time必须严格小于MySQL wait_timeout(建议设为wait_timeout−30),heartbeat≤max_idle_time(推荐max_idle_time/2),否则连接取出时必然失效或触发频繁PING;pool.wait_timeout推荐2.0~5.0秒,过小导致瞬时抖动满屏WaitTimeoutException,过大掩盖真实瓶颈。

Hyperf 连接池里 max_idle_time 和 MySQL wait_timeout 怎么配才不翻车
配反了会直接导致“MySQL server has gone away”反复出现,而且错误日志里看不出根源。关键不是心跳开没开,而是空闲连接在池里“睡过头”了。
-
max_idle_time必须严格小于 MySQL 的wait_timeout(查法:SHOW VARIABLES LIKE 'wait_timeout';),建议设为wait_timeout - 30 - 比如 MySQL 是
28800(8 小时),你设max_idle_time = 28800或更大——连接取出时必然失效 -
max_idle_time设太小(如10.0)会导致频繁建连销毁,压高 CPU 和 TCP TIME_WAIT - 别信“设成 0 就不回收”,Hyperf 不支持
0,会 fallback 到默认值(60.0),反而更难控
pool.wait_timeout 设 5.0 还是 0.5?看的是协程排队行为,不是数据库响应
pool.wait_timeout 控制的是“协程等连接”的上限,不是 SQL 执行超时。设错会掩盖真实瓶颈,或让请求无声失败。
- 设
0.5:瞬时流量抖动就满屏WaitTimeoutException,但实际连接池可能刚忙完正在归还 - 设
8.0+:请求卡住 10 秒才报错,你根本分不清是慢查询、连接泄漏,还是 max_connections 真不够 - 推荐值
2.0~5.0:给事务/批量操作留出缓冲,又能在连接耗尽时快速暴露问题 - 验证方法:
hyperf:pool-status查wait字段,持续 > 0 就得扩容,不是调超时
为什么开了 pool.heartbeat 还断连?它根本不是后台常驻心跳
pool.heartbeat 在 Hyperf 里只在连接被取出时触发一次 PING,不是后台线程轮询。真正负责空闲检测的是 IdleConnectionChecker,它只认 max_idle_time 和 heartbeat 的组合。
-
heartbeat值必须 ≤max_idle_time,推荐设为max_idle_time / 2(比如max_idle_time=270,则heartbeat=135) -
heartbeat设太小(如5):IdleConnectionChecker 每秒发大量PING,DB CPU 直接拉满 - 驱动不支持
PING(如某些老 pgsql 扩展)才考虑'checker' => 'SELECT 1',但该语句不能在事务中执行 -
min_connections别设0,冷启延迟高;生产建议 ≥2,避免首请求建连阻塞
Pool::refresh() 不是重连,是强制清池——正在跑的 SQL 会直接断
调用 Pool::refresh() 不会等当前 SQL 执行完,而是原子性释放所有连接 fd,切断协程绑定。这招适合修复连接状态错乱,但代价明确。
- 正在执行的请求会收到
Connection reset by peer或Broken pipe,必须上层捕获并重试 - 新请求会排队,排队时长由
pool.wait_timeout控制,不是立即可用 - 别在定时任务里无条件调用——若刚好撞上高峰,可能引发雪崩
- 更适合配合 DB 主从切换、密码轮转等确定性运维动作,而非“预防性刷新”
真正容易被忽略的点是:max_idle_time 和 wait_timeout 的差值必须稳定大于网络 RTT + 单次 PING 耗时,否则边缘场景下仍会取到半失效连接。



















