协程 MySQL 必须在协程上下文中运行,否则直接 new 或 connect 会报错“must be called in the coroutine”;需通过 Co::stats() 确认协程环境,Web 用 Octane/Swoole 服务、CLI 用 Co::run 包裹;连接不可跨协程复用,须每个协程独立 new+connect 或使用连接池;query() 返回 false 不一定表示错误,需配合 getLastError() 或开启 throw_exception;事务必须在单个协程内完成,begin/commit/rollback 不可跨协程。

协程 MySQL 必须在协程上下文中运行
直接 new Swoole\Coroutine\MySQL() 或调用 connect() 会报错 Swoole\Coroutine\MySQL::query(): must be called in the coroutine,因为底层 I/O 调度依赖协程环境。没启动协程就调用,不是“慢”,而是根本跑不起来。
确认方式:执行 php -r "echo Swoole\Coroutine::stats()['coroutine_num'] ?? 'no coro';",非零才表示当前在协程中。
- Web 场景:必须用
octane:start、swoole-http-server或Co\run()启动,不能走 PHP-FPM / Apache /php artisan serve - CLI 脚本:得包一层
Co\run(function () { ... }),否则connect()直接 throw - ThinkPHP/Laravel 中:config/database.php 的
'type'必须设为'mysql'(不是'pdo'),且不能在构造函数或服务提供者里提前触发查询
连接不能跨协程复用,否则触发 Socket#N is not available
每个 Swoole\Coroutine\MySQL 实例绑定创建它的协程上下文。把它存进全局数组、静态变量或传给另一个 go() 协程后调用 query(),会立刻 fatal:Fatal error: Uncaught Swoole\Error: Socket#N is not available。
这不是连接池失效,是协程调度器拒绝访问非当前协程持有的 socket 句柄。
- 正确做法:每个协程自己
new+connect(),或使用连接池管理器(如swoole/library提供的Pool类)分配独立实例 - 错误写法:
$pool[] = new Co\MySQL();然后在不同协程里 pop 出来用 —— 除非你确保 pop 和 use 在同一个协程内 - 连接池实现关键:
get()返回前必须调用connect()(可能复用已连但未 close 的句柄),put()前需检查是否仍可用($mysql->connected),不可直接close()
query() 返回 false 不代表出错,得配合 getLastError() 或 throw_exception
query() 返回 false 时,错误信息不一定在 getLastError() 里——它只对「查询执行失败」有效(比如 SQL 语法错、主键冲突)。而连接阶段失败(DNS 解析超时、TCP 握手失败)会直接抛出 Throwable,根本不会走到 query() 这步。
更麻烦的是,默认模式下 query() 失败只返回 false,不抛异常,容易静默失败。
- 推荐开启异常模式:
$mysql->set(['throw_exception' => true]),这样所有错误都统一走 try/catch - 若保持默认,必须严格判断:
if ($result === false) { echo $mysql->getError(), $mysql->getErrorCode(); } -
getLastError()是实例方法,多个连接共用一个变量名(如$db)时,容易误读上一个连接的错误值
事务必须在单个连接内完成,不能拆到多个协程
MySQL 事务依赖连接会话状态。begin()、commit()、rollback() 必须在同一个 Swoole\Coroutine\MySQL 实例上调用。如果把 begin() 放进一个协程,commit() 放进另一个,事务早已丢失上下文,commit() 实际什么都不会做。
常见陷阱:用 channel 控制并发时,误以为 “事务逻辑分散执行也行”——不行,整个事务链路必须串行跑在一个协程里。
- 正确结构:
go(function () use ($mysql) { $mysql->begin(); $mysql->query(...); $mysql->query(...); $mysql->commit(); }); - 高并发场景下,若事务耗时长,应限制并发数(如用
Channel控制最大 5 个事务协程同时跑),避免连接数打满或锁等待堆积 - 不要在事务中混用非协程客户端(如 PDO),会导致连接被阻塞,整个协程卡死


















