PHP-FPM下连接池根本不起作用,因进程隔离模型导致连接无法跨请求复用;真正生效仅限Swoole、RoadRunner等常驻内存环境。

连接池在 FPM 下根本不起作用
直接说结论:如果你用的是 PHP-FPM(绝大多数传统部署),pooling: true 配置会被 Doctrine 忽略,底层 PDO 连接不会复用,更谈不上池化。因为连接池依赖协程或虚拟线程的生命周期管理能力,而 FPM 是进程隔离模型 —— 每个请求结束,连接就销毁;下个请求重新连。你看到的“连接复用”只是 PDO 的 PDO::ATTR_PERSISTENT 持久连接,它共享的是进程内连接句柄,不是跨请求的连接池。
真正生效的环境只有两类:Swoole 5.0+(启用协程模式)、RoadRunner 或 ReactPHP 等支持长生命周期的异步运行时。确认方式很简单:运行 php --ri swoole 看是否启用了 coroutine => enabled;或者检查你的启动命令是不是 rr serve 而非 symfony server:start。
doctrine.yaml 中 pool 配置项怎么写才有效
配置必须满足三个硬性条件才能被 DBAL 加载:PHP ≥ 8.1、Doctrine Bundle ≥ 2.10、DATABASE_URL 中明确带 serverVersion(例如 mysql://user:pass@localhost/db?serverVersion=8.0)。否则即使写了 pooling: true,启动时也会静默跳过。
推荐写法(以 default 连接为例):
doctrine:
dbal:
url: '%env(DATABASE_URL)%'
options:
pooling: true
pool:
min_connections: 5
max_connections: 50
idle_timeout: 60
注意点:
-
min_connections是服务启动时预热的连接数,设太低会导致首波请求等待建连 -
max_connections不是“最多允许 50 个并发查询”,而是“池里最多存 50 个活跃连接”,超限请求会阻塞在get()阶段 -
idle_timeout单位是秒,建议设为数据库端wait_timeout的 70% 左右(比如 MySQL 默认 28800 秒,这里设 20000 即可),避免连接被远端主动断开后本地还傻等
多连接(default + reporting)怎么分别配池
不能只在顶层写 pool:,那样只对 default 生效。必须显式进入 connections 分组,为每个命名连接单独声明池参数:
doctrine:
dbal:
connections:
default:
url: '%env(DATABASE_URL)%'
pool:
min_connections: 10
max_connections: 30
reporting:
url: '%env(REPORTING_DATABASE_URL)%'
pool:
min_connections: 3
max_connections: 15
常见错误:
- 漏掉
pool:下的缩进,YAML 解析失败,报错Invalid type for path "doctrine.dbal.connections.default.pool" - 给 reporting 连接写了
options: pooling: true,但没写pool:块 —— DBAL 会认为你没启用池,走默认单连接逻辑 - 两个连接共用同一套数据库实例却设了过高
max_connections,加起来超过 MySQL 的max_connections限制,导致部分请求卡死在连接获取阶段
怎么验证连接池真的在工作
光看日志或响应时间没用。最直接的方式是查数据库当前连接数,并对比请求压力下的变化趋势:
登录 MySQL 执行:SHOW STATUS LIKE 'Threads_connected';
然后用 ab -n 1000 -c 100 http://your-app/api/endpoint 压测,观察该值是否稳定在你设的 min_connections 到 max_connections 区间内(比如压测中维持在 12~28 之间,而不是从 1 跳到 100+ 再回落)。如果始终只增不减、缓慢上涨,大概率是连接没归还 —— 检查是否有未关闭的 Connection 对象、或事务没提交/回滚导致连接被长期占用。
另一个信号是 Doctrine 日志里出现 Acquired connection from pool 和 Released connection to pool,这两个日志只在池模式下输出。开启方式:doctrine.dbal.logging: true,但生产环境别常开,只临时用于验证。
最容易被忽略的一点:连接池不解决慢查询问题。它只管“连得快、放得回”。如果某条 SQL 执行 3 秒,那这 3 秒内连接就被独占,其他请求只能排队等。所以调优顺序永远是:先优化 SQL 和索引,再配池;否则池子越大,积压越严重。


















