db::transaction()在协程环境下必然报错,因其底层依赖mysqli_commit()等函数式接口,而swoole协程mysql返回对象而非resource;必须弃用该方法,改用swoole\coroutine\mysql原生命令或think-swoole连接池手动事务控制。

Db::transaction() 在协程环境下必然报错,这不是配置问题,是 ThinkPHP 原生事务 API 与 Swoole 协程 MySQL 对象类型不兼容导致的硬伤。
Db::transaction() 报 Cannot use object of type Swoole\Coroutine\MySQL as resource 怎么办
- 这个错误说明你正在用传统 mysqli/PDO 风格的事务函数(如
mysqli_commit())去操作一个协程 MySQL 实例,而后者返回的是对象,不是资源(resource) - ThinkPHP 的
Db::transaction()底层依赖mysqli_commit()或PDO::commit(),但协程驱动(如Swoole\Coroutine\MySQL)不提供这些函数式接口 - 即使你在
config/database.php中设了'type' => 'mysql',只要用了think-swoole或手动启用了SWOOLE_HOOK_PDO,底层连接就可能被替换为协程对象,触发此错误 - 不要尝试绕过:禁用 hook、强制切回 PDO、或 patch
think\db\Connection—— 这些都会让协程优势归零或引发更隐蔽的连接泄漏
正确做法是彻底弃用 Db::transaction(),改用显式协程事务控制:
- 使用
Swoole\Coroutine\MySQL原生命令:$db = new \Swoole\Coroutine\MySQL(); $db->connect(['host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'database' => 'test']); $db->query('START TRANSACTION'); $result = $db->query('UPDATE user SET score = score + 1 WHERE id = 1'); if ($result) { $db->query('COMMIT'); } else { $db->query('ROLLBACK'); } - 若仍想用 ThinkPHP 的查询构造器,需配合
think-swoole提供的Db::pool()+ 手动 begin/commit:$pool = Db::pool('default'); $connection = $pool->get(); $connection->startTrans(); try { Db::table('user')->where('id', 1)->inc('score')->update(); $connection->commit(); } catch (\Throwable $e) { $connection->rollback(); throw $e; } finally { $pool->put($connection); }
$_SESSION 和 setcookie() 在 Swoole HTTP Server 中失效的原因
- Swoole 是常驻内存模型,
session_start()依赖 PHP-CGI 的生命周期自动管理 session 文件锁和 cookie 输出时机;协程中多次请求共享同一进程上下文,导致 session 文件被阻塞、cookie 被覆盖或丢弃 -
setcookie()本质是往响应头写Set-Cookie,但在 Swoole 的response对象里,header 必须在end()前显式调用,否则被忽略
必须切断原生 session 流程:
- 在入口文件或
WorkerStart回调中执行:ini_set('session.auto_start', '0') - 所有 session 操作改为手动控制:
- 每次请求开始时生成/复用
session_id(),再调用session_start() - 每次请求结束前必须调用
session_write_close(),释放文件锁
- 每次请求开始时生成/复用
- Cookie 绝对不要用
header('Set-Cookie: ...'),改用:$response->cookie('name', 'value', 3600, '/', '', false, true); - Session 存储必须切换为非文件驱动,推荐 Redis,并启用
session.use_strict_mode = 1防止协程间session_id冲突
Swoole::set(['hook_flags' => SWOOLE_HOOK_ALL]) 为什么没让 PDO 协程化
-
Swoole::set()的 hook 配置只对「后续新建」的连接生效;ThinkPHP 的think\db\Connection在容器初始化阶段就执行了new \PDO(...),这个连接创建发生在 hook 设置之前,完全绕过了协程 Hook 机制 - 更关键的是:即使你提前设置了 hook,如果框架在
WorkerStart之前就完成了数据库连接实例化(比如在App构造或服务注册时),那这个连接永远是同步的 -
SWOOLE_HOOK_PDO本身只拦截new \PDO()调用,不接管已存在的 PDO 实例;它也不影响mysqli_connect(),除非你额外加SWOOLE_HOOK_MYSQLI
唯一可靠路径是延迟连接 + 连接池:
- 禁用 ThinkPHP 默认的自动连接:在
config/database.php中设'deploy' => 0和'lazy' => true - 使用
think-swoole的连接池能力,在WorkerStart中初始化:Db::setConfig([ 'type' => 'swoole', 'pool_size' => 20, 'hostname' => '127.0.0.1', 'username' => 'root', 'password' => '123456', 'database' => 'test' ]) - 确保所有数据库操作都走
Db::table()->select()等查询构造器,而不是直接 new Connection
协程集成最易被忽略的点,不是怎么“开”,而是怎么“关”——关掉原生 session、关掉自动 PDO 初始化、关掉事务封装层。这些关不干净,协程带来的并发提升会立刻被同步阻塞拖垮。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











