必须用Swoole\Coroutine\Channel而非普通数组,因其协程安全、自动阻塞唤醒;容量宜设32~64,需匹配数据库max_connections并压测调优;归还前须rollback、RESET SESSION、健康检测,避免状态污染与连接泄漏。

连接池初始化必须用 Swoole\Coroutine\Channel 而不是普通数组
直接用 PHP 数组存连接对象,在协程并发下会出竞态问题:多个协程同时 pop 或 push 同一个数组,结果不是丢连接就是覆盖。Swoole 的 Channel 是协程安全的队列,底层由内核调度,pop 会自动阻塞等待,push 会唤醒等待协程。
常见错误是把 Channel 当成普通容器,不设容量或设得过大:
-
new Swoole\Coroutine\Channel(0)—— 容量为 0 表示无缓冲,push立即阻塞,除非有另一个协程在pop,极易卡死 - 容量远超后端服务承受能力(比如设 512,但 MySQL 的
max_connections只有 200),会导致大量连接被拒绝或超时 - 推荐初始容量设为 32~64,再根据压测 QPS 和
show processlist中活跃连接数动态调优
PDOPool/MysqliPool 的 $size 参数不是越大越好
官方文档写默认 64,但实际中这个值要和业务请求特征、数据库配置、协程生命周期严格对齐。它不是“最多能建多少连接”,而是“池里最多保留多少空闲连接”。超出部分的连接会在归还时被主动 close。
容易踩的坑:
- 事务中归还连接 ——
$pool->put($conn)前没检查$conn->inTransaction(),导致下一个协程拿到带事务状态的连接,执行SELECT时可能读到未提交数据,或INSERT报错 - 异常连接没清理 —— 比如网络闪断后
$conn->query()抛异常,但没调$pool->put(null),池子数量失衡,后续get()会一直阻塞 - 连接复用前缺少健康检测 —— 比如用
$conn->getAttribute(PDO::ATTR_CONNECTION_STATUS)或发个PING包,否则可能拿到已断开的连接直接失败
连接工厂函数必须处理重连与上下文隔离
连接池的构造器(传给 PDOPool 或手写 Channel 初始化循环的那个 callable)不能只做一次 new PDO()。它要负责:连接失败时重试、设置统一字符集、关闭自动提交、禁用预处理模拟(PDO::ATTR_EMULATE_PREPARES => false)。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
关键点:
- 不要在工厂里设置
PDO::ATTR_ERRMODE为EXCEPTION—— 这会让所有 SQL 错误都抛异常,掩盖连接层问题;建议设为WARNING,让业务层自己判断 - 每个连接必须独立设置
PDO::MYSQL_ATTR_INIT_COMMAND,比如SET NAMES utf8mb4,否则不同协程共用连接时可能因会话变量污染导致乱码或时区错误 - 如果用
MysqliPool,记得调$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, 3),避免 DNS 卡住整个池子
归还连接前必须清空事务和临时状态
连接池的本质是复用,但数据库连接不是无状态的。一个连接上残留的事务、用户变量、临时表、SQL_MODE,都会影响下一个使用者。
实操建议:
- 归还前强制 rollback:用
if ($conn->inTransaction()) { $conn->rollback(); },别依赖业务代码保证事务结束 - 清除用户变量:
$conn->exec("RESET SESSION")(MySQL 5.7+)或至少SET @var = NULL批量清空 - 不要在连接上创建临时表 —— 临时表生命周期绑定连接,归还后仍存在,下次使用时
CREATE TEMPORARY TABLE IF NOT EXISTS会静默失败 - 若业务强依赖某类会话变量(如
sql_mode),应在获取连接后立即重置,而不是在工厂里设死
最常被忽略的是连接空闲超时后的自动清理逻辑 —— Channel 本身不提供 TTL,得靠定时协程扫描并 close 长期未归还的连接,否则内存泄漏和连接数虚高会悄无声息地拖垮服务。

















