thinkphp本身不内置数据库连接池,fpm模式下所有pool配置均被忽略;仅在swoole协程环境下,通过swoole\coroutine\mysql及deploy=>1配置才可能实现连接复用。

ThinkPHP 本身没有内置连接池,所谓“开启连接池”其实是依赖运行环境是否支持连接复用——FPM 下不可能有真正意义的连接池,只有 Swoole 协程或 Workerman 这类常驻进程模型中,才可能让连接在多个请求间复用。
为什么 database.php 里配了 'pool' => ['min' => 5] 却没效果
因为 TP 不解析这个配置。'pool' 键在原生 FPM 环境下被完全忽略;即使你写 'pool' => ['enable' => true],框架也不会初始化任何池管理器。它只在 Swoole 协程启动(如 php think swoole)且驱动为 mysql 时,才通过 Swoole\Coroutine\MySQL 实现底层连接复用。FPM 模式下,Db::connect() 每次都 new 一个 PDO 实例,生命周期仅限当前请求。
常见误操作包括:
- 在 FPM 下改
config/database.php中的pool配置,期待连接数下降 - 用
Db::connect(['hostname' => '...'])动态传配置,绕过连接缓存,导致无法复用 - 开了
'debug' => true,框架会禁用所有连接复用逻辑,每次查都新建
deploy => 1 是什么,和连接池有什么关系
deploy => 1 不是连接池开关,而是连接复用的基础机制:它让同 host/port/db/user/password 的配置共享同一个 PDO 实例(单例缓存),避免重复创建对象。它在 FPM 和 Swoole 下都生效,但只解决“同一请求内多次查询复用”,不解决跨请求复用。
关键点:
- 必须保证配置项完全一致(比如
'host'写成'hostname'就算不同配置) - 它不控制连接是否真实断开,只是复用 PDO 对象;连接仍会在请求结束时由 PHP 自动 close
- CLI 或长任务中,若没手动
Db::close(),这个复用反而会导致连接堆积
怎么确认当前真用了协程连接池
别信配置,看运行时行为。最直接的方法是在查询前打日志:
Log::info('conn id: ' . spl_object_id($connection));
发起两个快速请求(确保落在同一个 Swoole worker + 同一协程上下文),如果两次日志里的 spl_object_id 相同,且连接未被 close(),说明进了池;如果不同,大概率还在走新连接逻辑。
辅助验证手段:
- 启动后执行
lsof -i :3306 | wc -l,对比 FPM 模式下的连接数,差值明显才说明池起了作用 - 在 Swoole 配置中加
'options' => ['heartbeat' => 3],然后观察 MySQL 的SHOW PROCESSLIST是否有稳定连接持续存在 - 事务中调用
Db::transaction()后再查一次,如果连接 ID 变了,说明事务强制占用了独立连接,未归还到池
连接复用率低的典型原因和绕过方案
即使开了 Swoole + deploy => 1,复用率仍可能很低,常见原因:
- 在循环里反复写
Db::table('user')->where(...)->find(),每次都会触发新连接获取逻辑 - 用了
Db::connect('mysql2')切库,但没定义mysql2配置,框架 fallback 到默认连接,却因配置名不同无法复用 - 事务未正确
commit或rollback,连接被标记为占用,后续请求只能新建 - 协程中混用同步函数(如
sleep()、file_get_contents()),导致协程切出,连接被丢弃
临时绕过建议:
- 批量操作优先用
whereIn('id', $ids)->select(),而不是循环find() - 手动复用连接:
$conn = Db::connect(); $conn->table('user')->select(); - CLI 或定时任务中,显式调用
Db::close()或unset($db)
真正难处理的是跨协程、跨事务、多库混合场景下的连接状态隔离问题——这些不是配个参数能解决的,得靠代码层收敛连接使用路径,避免隐式新建。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











