Hyperf的MySQL连接池天然按Worker进程隔离,不存在跨进程共享;每个Worker独立初始化DbPool实例,连接、队列、心跳互不干扰,pool.max_connections和min_connections均为每进程上限。

Hyperf 协程 MySQL 连接池不是“多进程共享”的
直接说结论:Hyperf 的 MySQL 连接池**天然按 Worker 进程隔离**,不存在跨进程共享连接池这回事。所谓“共享”,其实是每个 Worker 进程各自持有一个独立连接池实例,共用同一套配置参数,但连接对象、空闲队列、心跳线程都互不干扰。
误以为能“共享”常导致两个典型问题:一是调高 pool.max_connections 后 DB 实例爆 Too many connections;二是监控看到 pool.status 中 used 峰值很低,却频繁出现 wait > 0 —— 实际是单个 Worker 池已满,其他 Worker 池还有空闲,但请求无法调度过去。
- Hyperf 启动时,每个 Swoole Worker 进程会初始化自己的
DbPool实例,min_connections和max_connections都是**每进程上限** - 连接池生命周期绑定 Worker 进程:进程重启(如 reload)、协程崩溃、或
max_idle_time触发回收,都只影响本进程内的连接 - 没有跨进程连接复用机制,也不存在“主池 + 子池”或“全局连接句柄表”这类设计
如何让多个 Worker 共同分担数据库压力?
不能靠“共享连接池”,但可以通过合理配置和流量分配,让整体连接资源被高效利用。关键不是池子共不共享,而是池子够不够、配得对不对、用得准不准。
-
pool.max_connections必须按 **Worker 数 × 单 Worker 并发峰值 × 1.2** 估算,再对比 MySQL 实例的max_connections(查SHOW VARIABLES LIKE 'max_connections';),确保总和 ≤ DB 侧限制的 70% -
pool.min_connections建议设为2而非0或1:冷启动时每个 Worker 至少有 2 个预热连接,避免首请求触发建连延迟 - 使用
hyperf:pool-status命令分别查看各 Worker 的池状态(需配合--worker-id参数),重点关注wait是否持续 > 0 —— 这说明该 Worker 的池容量不足,而非“其他 Worker 池没被利用” - 若发现某些 Worker
wait高、另一些used很低,大概率是负载不均,应检查 Swoole 的 dispatch_mode(推荐3,即IP hash或round robin)和上游网关路由策略
为什么 heartbeat 设了却还报 “MySQL server has gone away”?
这不是连接池“没共享”导致的问题,而是心跳机制根本就没按预期工作。Hyperf 的 pool.heartbeat 默认不生效——它只在连接从池中取出时做一次 PING,不是后台常驻探测。
真正维持连接活性的是 IdleConnectionChecker 线程,它依赖三个参数协同:
-
pool.max_idle_time必须严格小于 MySQL 的wait_timeout(查法:SHOW VARIABLES LIKE 'wait_timeout';),建议设为wait_timeout - 30 -
pool.heartbeat应 ≤max_idle_time,推荐设为max_idle_time / 2(例如max_idle_time=270,则heartbeat=135) -
pool.min_connections ≥ 2:否则空闲检查线程可能因无连接可检而跳过,导致失效连接滞留 - 驱动必须支持
PING;若不支持(如某些旧版pdo_mysql),可改用'checker' => 'SELECT 1',但注意该语句不能在事务中执行
读写分离场景下,连接池更不能“共享”
读写分离本质是多个逻辑上独立的数据源,Hyperf 要求每个库必须定义独立连接池,且命名只能小写字母+下划线(如 read_pool_1)。一旦命名含点号、大写或非法字符,Db::connection('read.pool') 就会抛出 Connection [read.pool] not found 错误。
- Model 层用
on('read_pool_1')只影响当前查询,不影响后续操作;$connection = 'read_pool_1'仅对静态方法生效,new User()->save()仍走默认池 - 原生查询必须先
Db::connection('read_pool_1'),再链式调用table()等,顺序颠倒就无效 - 事务
Db::transaction()严格绑定单池,跨db.write和read_pool_1会直接报错,MySQL 从库本身就不支持BEGIN - 别把多个 MySQL 版本(如 5.7 和 8.0)塞进同一个池:认证协议不同,部分连接会在 handshake 阶段卡死
连接池不是黑盒,它的每个参数都在 Worker 进程内独立起效;配错 max_idle_time 和 wait_timeout 的差值,比配错 max_connections 更容易引发静默故障 —— 连接看似“活着”,取出来一用就断。



















