发布于2026-07-04 阅读(0)
扫一扫,手机访问
在Swoole常驻内存环境下,数据库连接复用这件事儿,可不能还抱着PHP-FPM那种“一个请求建一条连接”的老思路。否则每个协程都各自新建TCP连接,MySQL很快就会被挤得报出Too many connections——说白了,不配置连接池,复用就只是空谈。
先跑一句 php --ri swoole,确认输出里有 version => 4.8.0 或更高。低于这个版本,PDO/MySQLi 就没法协程化,强行启用也只会退化成同步阻塞,白忙一场。
再跑 composer show topthink/think-swoole,确保版本 ≥ 3.1.0。旧版不支持 DatabasePool 自动做协程ID隔离,连接污染迟早找上门。
特别提醒:hostname、username、password 必须明文写死在 config/database.php 里,不能用 env() 读取——协程上下文切换时环境变量不可靠,真碰上连接初始化失败,它会悄无声息地降级,调试起来比翻车还痛苦。
打开 config/database.php,找到主数据库配置块(通常是 'default' => 'mysql' 下面的 'mysql' 子数组),按下面四步操作:
'type' => 'pdo_mysql' 改成 'type' => 'mysql'——强制走 Swoole 原生协程 MySQL 客户端,PDO 驱动在协程事务状态管理上不兼容;'deploy' => 1(或未定义)改成 'deploy' => 0——关掉读写分离,否则 Connection 类内部会绕过协程驱动,偷偷创建同步 PDO 连接;'params' 数组里加上 PDO::ATTR_EMULATE_PREPARES => false——禁用预处理模拟,不然 SQL 被 PHP 同步拼接,协程化的效果直接归零;PDO::ATTR_PERSISTENT => true 相关的配置——它在 Swoole 里完全无效,还可能干扰连接池的健康检测。在 app/provider.php 末尾追加以下代码:
use thinkswoole\pool\DatabasePool;
use thinkswoole\pool\ChannelPool;
Container::getInstance()->bind('db', function ($app) {
$config = $app->config->pull('database');
return new DatabasePool($config, new ChannelPool(60));
});
这里的 60 是连接池大小,一般按 max_request * 2 来估算(比如 Swoole 配置了 max_request=3000,设 60~100 比较稳妥)。太大容易撑爆内存,太小又会频繁创建销毁连接,起不到复用效果。
关键提醒:必须显式调用 Db::pool()->table(),直接用 Db::table() 走的仍然是传统单例连接——这是最容易被忽视的坑,90% 的“复用失败”都是因为这个调用入口没改过来。
启动 Swoole 服务后,执行 mysql -u root -p -e "show processlist;",观察 Id 列的数字是否稳定在池大小附近(比如 60),而不是随着并发请求数一路猛涨。
在业务代码里加一行日志:Log::info('conn uid: ' . Swoole\Coroutine::getuid() . ', conn hash: ' . spl_object_hash($connection));,然后发起两个并发请求,检查日志:两次的 conn hash 应该不同,但 conn uid 各自独立——这说明协程 ID 隔离生效了。
如果 show processlist 里的连接数还在持续攀升,必须立刻排查:是不是所有 DB 操作(包括模型 sa ve、事务 DB::transaction)都已经替换成了 Db::pool() 前缀。这一步漏了,前面的配置就全白费了。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8