Yii2+ Swoole中协程MySQL连接池需自行实现,因Yii2不支持协程、PDO阻塞,须绕过DB组件直接使用Swoole\Coroutine\MySQL或封装池,在onWorkerStart初始化,协程内安全调用并手动转义SQL参数。

协程 MySQL 连接池在 Yii2 + Swoole 中必须自己实现
Yii2 官方不支持协程,yii\db\Connection 是同步阻塞的,直接丢进 Swoole 协程环境会退化成同步 IO,连接池也无从谈起。Swoole 的 Swoole\Coroutine\MySQL 是纯协程驱动,和 Yii2 的 PDO 层完全不兼容——不能混用,也不能简单替换 PDO 驱动。
常见错误现象:Call to undefined method Swoole\Coroutine\MySQL::query() 或查询卡死、连接数暴涨;本质是误把协程客户端当成了 PDO 兼容层。
- 必须绕过 Yii2 的 DB 组件,单独用
Swoole\Coroutine\MySQL或封装好的协程连接池(如swoole/mysql-pool)管理连接 - 连接池需在 Swoole Server 启动前初始化(如
onWorkerStart),不能在每次请求中 new - 不要尝试 patch
yii\db\Connection,PDO 扩展本身不支持协程,底层仍是阻塞 syscall
怎么安全地在 Yii2 请求生命周期里调用协程 MySQL
不能在控制器 action 里直接 go(function () { ... }) 就完事——Swoole 协程必须在协程上下文里运行,而 Yii2 默认不是。必须确保整个请求处理链路跑在协程内,否则 Swoole\Coroutine\MySQL 会报 Co::sleep() must be called in coroutine 类似错误。
使用场景:适合高频、轻量、IO 密集型查询(如用户信息校验、配置拉取),不适合复杂事务或长耗时写操作(协程里做事务需手动控制 commit/rollback,且无法和 Yii2 ActiveRecord 事务联动)。
- 入口必须用
Swoole\Http\Server+onRequest回调,并在回调里go(function () { ... })启动协程 - Yii2 应用实例(
yii\web\Application)要包裹在协程内启动,但注意:Yii::$app是全局静态,多个协程并发访问同一实例可能引发状态污染 - 推荐做法:协程内只做数据查询,查完结果后退出协程,再交由 Yii2 渲染响应(即“协程查,同步渲”),避免深入侵入 Yii2 生命周期
Swoole\Coroutine\MySQL 查询参数和错误处理要点
它不支持预处理语句的占位符自动绑定(如 :name 或 ?),所有参数必须手动拼进 SQL 字符串,且需自行转义——否则直接 SQL 注入。这不是疏忽,是协程驱动的极简设计取舍。
性能影响明显:不用 prepare 可省掉一次服务端解析,但代价是更重的手动防护;兼容性上,它只支持 MySQL 协议,不兼容 PostgreSQL / SQLite。
- 必须用
$mysql->escape($value)处理每个动态值,不能依赖框架的addParams() - 查询失败时返回
false,错误信息在$mysql->error,错误码在$mysql->errno,不是抛异常 - 超时默认是 0.5 秒,可通过
connect()的timeout参数调整,但设太长会拖垮协程调度 - 示例:
$res = $mysql->query("SELECT * FROM user WHERE id = " . $mysql->escape($id));
连接池泄漏和复用失败的典型原因
协程连接池不是“用了就还”,而是“协程结束自动归还”。但如果你在协程里启动了子协程、用了 defer、或忘了 yield 等待 IO,连接可能被卡住不释放,池子里的连接数缓慢涨到上限,后续请求全卡在 get() 上。
容易被忽略的地方:连接池对象本身是共享的,但每个 $pool->get() 返回的 Swoole\Coroutine\MySQL 实例**仅限当前协程使用**,跨协程传递或存到全局变量会导致不可预知行为(比如查询返回空、连接静默断开)。
- 永远不要把
$mysql存到static变量或Yii::$container里 - 务必在
try/finally块里确保$pool->put($mysql)被执行,哪怕发生异常 - 连接池最大连接数(
maxIdle/maxActive)建议按压测结果设,盲目调高反而增加 MySQL 侧压力 - 用
swoole_server->stats()查看当前协程数、活跃连接数,比日志更能定位泄漏点
事情说清了就结束










