根本原因是heartbeat默认不生效,仅在连接取出时单次ping,真正起效的是idleconnectionchecker线程,需max_idle_time严格小于mysql wait_timeout(建议设为wait_timeout-30)、heartbeat≤max_idle_time(推荐max_idle_time/2),且min_connections≥2、驱动支持ping;配错将导致连接失效或无效探测。

为什么开了 pool.heartbeat 还报 “MySQL server has gone away”
根本原因不是没心跳,而是 heartbeat 参数在 Hyperf 里默认不生效——它只在连接被取出时做一次 PING,不是后台常驻探测。真正起作用的是空闲连接检查线程(IdleConnectionChecker),它依赖 max_idle_time 和 heartbeat 协同工作。
-
max_idle_time必须严格小于 MySQL 的wait_timeout(查法:SHOW VARIABLES LIKE 'wait_timeout';),建议设为wait_timeout - 30 -
heartbeat值应 ≤max_idle_time,推荐设为max_idle_time / 2(比如max_idle_time=270,则heartbeat=135) -
min_connections别设 0,否则冷启延迟高;建议 ≥ 2 - 驱动不支持
PING时才考虑'checker' => 'SELECT 1',但该语句不能在事务中执行
PostgreSQL 报 “Too many clients” 不是驱动版本问题
这个错误(致命错误: 对不起, 已经有太多的客户)是 PostgreSQL 服务端明确拒绝新连接的信号,源头在 postgresql.conf 的 max_connections 被耗尽,或客户端连接未归还。
- 先查 DB 实际限制:
SHOW max_connections;(注意不是 MySQL 语法) - Hyperf 的
pool.max_connections建议 ≤ DB 侧限制的 70%,预留空间给备份、监控等 -
pool.wait_timeout生产环境建议 ≥ 5.0 秒,太小会导致请求排队失败,日志满屏wait timeout - 盲目升级
ext-pgsql或hyperf/database-pgsql可能引入协程调度 bug,如aio_thread failed
max_idle_time 和 wait_timeout 配反了会怎样
配错组合会掩盖真实断连,甚至让问题更难定位:
- 若
max_idle_time > wait_timeout(比如 MySQL 是 60s,你设了 120s),连接在池里“睡过头”,取出来时必然已失效 - 若
heartbeat设太小(如 5s),后台每秒发大量PING,压垮数据库;设太大(如 300s),等于没检测 -
pool.wait_timeout(等待超时)和 MySQL 的wait_timeout是两回事:前者是客户端等连接的秒数,后者是服务端关闭空闲连接的秒数 - PostgreSQL 没有
wait_timeout,对应参数是tcp_keepalives_idle,别混用
连接池配置里最容易被忽略的三个细节
这些点不写进文档,但一错就卡住上线节奏:
-
pool.connect_timeout默认 10.0 秒,但线上网络抖动时建议降到 5.0,避免阻塞协程调度器 -
options.ATTR_TIMEOUT(PDO 层超时)必须显式设置,如'options' => [PDO::ATTR_TIMEOUT => 3],否则某些慢查询可能 hang 死连接 - 连接泄露比连接数不够更危险:没在
finally或协程 defer 里调用release(),池子会慢慢枯竭,指标hyperf_db_pool_used_connections会长期贴着上限跑
实际生效的配置要靠三者对齐:MySQL/PostgreSQL 服务端参数、Hyperf 连接池参数、PHP 驱动行为。少对齐一个,心跳就成摆设,超时就变玄学。











