根本原因是ThinkPHP的think\db\Connection在初始化时硬创建PDO连接,绕过Swoole协程Hook;需在WorkerStart中设SWOOLE_HOOK_PDO、启用think-swoole连接池、禁用Db::transaction()改用适配方法,并切换Session/Redis为协程友好驱动。

为什么 Swoole::set() 里配了 hook_flags 还是没协程化 MySQL?
根本原因不是配置漏了,而是 ThinkPHP 默认的 think\db\Connection 在初始化时就用 new \PDO() 硬创建连接,绕过了 Swoole 的协程 Hook。Swoole 的 PDO 协程化只对运行时新建立的连接生效,不接管已存在的同步连接实例。
- 必须在
Swoole::set()中启用SWOOLE_HOOK_ALL或至少SWOOLE_HOOK_PDO,且要在WorkerStart回调里设,不能在全局提前设(否则可能被框架覆盖) - ThinkPHP 的数据库连接池(如
think-swoole扩展)需显式启用,仅靠 Swoole Hook 不足以让Db::table()->select()自动走协程 PDO - 检查是否误用了
think\db\connector\Mysql的connect()方法——它内部调用的是原生mysqli_connect,而 Swoole 默认不 Hook mysqli(除非手动加SWOOLE_HOOK_MYSQLI)
使用 think-swoole 时,Db::transaction() 报 Cannot use object of type Swoole\Coroutine\MySQL as resource
这是典型的协程 MySQL 对象和传统事务 API 冲突:ThinkPHP 的事务控制依赖 mysqli_commit()/mysqli_rollback() 这类函数式调用,但协程 MySQL 返回的是对象,不兼容资源类型。
- 禁用
Db::transaction(),改用$db->startTrans()+$db->commit()/$db->rollback()—— 这套方法在think-swoolev4+ 中已适配协程 MySQL 实例 - 确保
config/database.php中'type' => 'mysql'且未强制指定'use_pdo' => true(PDO 模式下事务行为更不可控) - 若仍报错,检查是否混用了
think-orm和旧版think-db,二者事务接口不一致,协程适配层有差异
Swoole\Http\Server 启动后,Cookie 和 Session 失效或跨请求丢失
因为 Swoole 是常驻内存服务,PHP 原生的 session_start() 和 setcookie() 依赖 CGI 生命周期,而在协程中多次请求共享同一个 PHP 进程上下文,导致 session 文件锁、cookie 输出时机错乱。
- 必须关闭
session.auto_start,所有 session 操作改为手动控制:session_id($id)+session_start(),并在每次请求结束前调用session_write_close() - Cookie 不要依赖
header('Set-Cookie: ...'),改用$response->cookie()(think-swoole提供)或直接操作$server->response->cookie() - Session 存储必须切换为非文件驱动,推荐
redis并启用session.use_strict_mode = 1,避免协程间 session_id 冲突
为什么 Redis 协程客户端在 ThinkPHP 中总退回到同步阻塞?
ThinkPHP 默认使用的 think-cache 或 cache.redis 配置走的是 Predis 或原生 Redis 扩展,这两者都不原生支持 Swoole 协程。即使启用了 SWOOLE_HOOK_REDIS,也只对 new Redis() 生效,不覆盖框架封装的缓存驱动。
立即学习“PHP免费学习笔记(深入)”;
- 必须替换缓存驱动:用
swoole/redis客户端(即Swoole\Coroutine\Redis)重写think-swoole的RedisCache类,或直接配置'type' => 'swoole_redis'(需自定义驱动) - 禁用
redis.pconnect,协程环境下长连接由 Swoole 自动管理,pconnect反而引发连接复用混乱 - 注意
Swoole\Coroutine\Redis不支持管道(pipeline)和事务(multi),若业务强依赖,得降级为连接池 + 同步 Redis 客户端
AppInit、HttpEnd)里执行的同步 I/O,会在协程上下文中阻塞整个 Worker。


















