pmmp/yii2-connection-pool 是专为 Swoole 协程设计的 PDO 连接池扩展,需在协程上下文手动初始化、单例注入 DI 容器,并严格通过 get()/put() 管理连接;不适用于 PHP-FPM/Apache,且无内置健康检查。

Yii2 本身没有内置连接池,pmmp/yii2-connection-pool 是第三方扩展
Yii2 的 yii\db\Connection 默认每次请求新建/复用 PDO 连接,但底层不维护连接池——它依赖 PHP-FPM 进程生命周期或 Swoole 协程环境下的连接复用机制,不是传统意义上的“连接池”。pmmp/yii2-connection-pool 是为 Swoole(尤其是协程模式)设计的轻量封装,本质是用 Swoole\Coroutine\Channel 管理预创建的 PDO 实例。
常见错误现象:Fatal error: Uncaught Swoole\Error: Unable to create channel 或连接数远超预期,多因未在协程上下文初始化、或在非 Swoole 环境(如普通 PHP-FPM)中强行启用。
- 只适用于 Swoole >= 4.5 + 协程模式(
enable_coroutine => true),PHP-FPM / Apache 下直接报错或静默失效 - 必须在协程启动后、首次数据库操作前完成连接池初始化,不能放在
config/web.php顶层执行 -
maxIdle和maxActive不是 MySQL 的max_connections,而是池内对象数量,设太高会撑爆内存,太低则频繁创建销毁抵消性能收益
pmmp/yii2-connection-pool 的正确初始化时机和位置
它不能像原生 db 组件那样在配置文件里声明即生效。必须手动在协程入口(如 Swoole HTTP Server 的 onRequest 回调、或 Yii2 的 Bootstrap 类中检测协程环境后调用)。
典型错误:在 common/config/main.php 中直接 new Pool() —— 此时无协程上下文,Channel 创建失败;或在控制器 action 里每次 new 一个池,导致连接泄漏。
- 推荐在自定义
Bootstrap类的bootstrap()方法中,用if (Swoole\Coroutine::getCid() > 0)判断是否已进入协程 - 池实例应单例化并注入到 DI 容器,避免重复初始化;例如绑定为
'dbPool',后续通过Yii::$app->get('dbPool')获取 - 初始化时传入的
config数组需包含完整 PDO DSN 信息,且username/password必须显式传入,不能复用原db组件配置里的敏感字段(因组件可能已被初始化)
从连接池获取连接的实际写法和常见漏点
拿到池对象后,不能直接调用 open() 或 createConnection(),必须用 get() 并配合 try/finally 归还——这是最容易被忽略的一步,漏掉会导致连接永远卡在池中,池迅速耗尽。
示例片段:
$pool = Yii::$app->get('dbPool');
$conn = $pool->get(); // 阻塞直到有空闲连接
try {
$stmt = $conn->prepare('SELECT * FROM user WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetchAll();
} finally {
$pool->put($conn); // 必须放回!不可省略
}
-
$pool->get()在池空时默认阻塞,可传timeout参数(单位秒)避免无限等待,例如$pool->get(0.5) - 不要对返回的
$conn调用close()或__destruct(),它不是真实 PDO 连接,只是池管理的代理对象 - 若在事务中使用,需确保整个事务块都在同一
$conn上执行,且put()放在事务提交/回滚之后
性能与兼容性边界:什么时候不该用这个包
它解决的是高并发协程场景下 PDO 创建开销问题,但代价是内存占用和逻辑复杂度上升。很多项目其实根本不需要。
- QPS PDO::ATTR_PERSISTENT => true)+ MySQL 连接复用已足够,加池反而增加调度负担
- 使用
yii2-redis或其他非 PDO 数据源时,该包完全不适用——它只包装 PDO,不涉及任何 Redis、MongoDB 等协议 - 若项目混用 Swoole 协程和同步阻塞逻辑(比如部分命令行任务仍走传统 FPM 模式),必须严格隔离配置,否则
get()在非协程环境会死锁
最常被忽略的点:这个包不处理连接健康检查。如果 MySQL 主动断开空闲连接(wait_timeout),池里的连接可能变成“僵尸”,需要自己加 PDO::ping() 或设置 validationQuery 做探活——但它本身没提供这个钩子。


















