ThinkPHP原生不支持数据库连接池,PDO持久化仅实现进程内复用,无连接数限制、排队等待或跨请求复用;真要池化必须满足两个硬条件:运行环境为Swoole(≥v4.8.0)且安装启用think-swoole(≥v3.1.0)的db_pool功能。

ThinkPHP 原生不支持数据库连接池管理,所谓“持久连接”只是 PDO 层级的进程内复用,不是连接池。 它无法限制连接数、排队等待、健康检查或跨请求复用,高并发下极易触发 Too many connections 或 MySQL has gone away。真要池化,必须换环境、换组件。
为什么 Db::connect() + persistent => true 不是连接池
开启 'persistent' => true 后,TP 仍每次调用 Db::connect() 都新建一个 PDO 实例(哪怕复用底层 socket),不共享、不限流、不回收空闲连接。关键问题在于:
- FPM 模式下:每个子进程维护自己的“持久连接”,重启即失效,
max_connections是 MySQL 全局上限,但 TP 完全不感知也不控制实际创建数量 - 连接空闲超时由 MySQL 的
wait_timeout决定(默认 8 小时),而 PHP 请求通常几秒就结束,下次复用时可能已断开,报MySQL server has gone away - 没有连接归还机制:事务未提交、游标未关闭、异常未捕获,连接就卡住不释放,形成泄漏
想用连接池?必须满足两个硬条件
缺一不可:
-
运行环境必须是 Swoole(≥ v4.8.0),FPM 下所有池配置、
Db::pool()调用全部无效 -
必须安装 think-swoole(≥ v3.1.0),并启用
db_pool功能;原生 TP 的database.php里加任何pool字段都无意义
验证是否生效:在控制器中打印 get_class(Db::pool()),应为 think\swoole\pool\DatabasePool;若仍是 think\db\Connection,说明没走池子。
立即学习“PHP免费学习笔记(深入)”;
Db::pool() 的正确用法和常见绕过点
即使启用了池,大量代码仍会绕过它走传统连接:
- 直接调用
Db::table()、Db::name()—— 默认用非池化连接 - 使用
Db::transaction()—— 默认不走池,必须显式写Db::pool()->transaction() - 模型操作(
UserModel::find())—— 默认绑定的是普通Db实例,需手动注入池化连接或重写模型的db()方法 - 读写分离场景下,
Db::connect('slave')不等于Db::pool()->connect('slave'),后者才进池
安全写法示例:Db::pool()->table('user')->where('id', 1)->find();别省略 pool()。
连接池参数设置不当的典型后果
配置在 config/swoole.php 的 db_pool 节点下,但几个参数容易配错:
-
max_active => 10:表示单个 Worker 进程最多持 10 条连接。若swoole.server.worker_num是 4,则整个服务最多 40 条连接 —— 但 MySQL 的max_connections若只设 30,多余请求直接被拒 -
wait_timeout => 2000:协程等待空闲连接超时时间(毫秒)。设太小(如 100)会导致大量PoolTimeoutException;设太大(如 10000)会让慢请求拖垮整体响应 -
idle_timeout => 60:空闲连接存活秒数。它不影响创建速率,只决定“冷连接”多久被回收;真正控速靠max_active
最容易被忽略的是:Swoole 每个 Worker 进程都有独立池子,预热必须在 WorkerStart 事件里做,否则只有第一个 Worker 有连接,其余 Worker 首次请求仍阻塞建连。



















