ThinkPHP 6.x 默认无跨请求连接池,仅单次请求内复用 PDO 连接;问题多因连接未释放或配置不当,需通过 PDO 实例哈希与 MySQL 线程 ID 验证真实复用,Swoole 环境须显式管理连接生命周期。

ThinkPHP 6.x 默认不支持连接池,Connection 是单次请求生命周期复用
ThinkPHP 没有原生连接池实现,所谓“连接复用”实际指 PDO 连接在单次 HTTP 请求内被多次 Db::table() 或 Model::find() 复用——不是跨请求的长连接池。如果你在日志里看到大量 SQLSTATE[HY000] [2002] Connection refused 或连接数飙升到 MySQL max_connections 上限,那问题不在“复用率低”,而在连接没被及时释放或连接配置不合理。
实操建议:
- 确认是否启用了
deploy配置:只有'deploy' => 1且'rw_separate' => true时,读写分离才会真正复用底层 PDO 实例;否则每次Db::connect()都可能新建连接 - 避免在循环中反复调用
Db::connect(['host' => 'xxx'])—— 即使参数相同,TP 也不会自动复用已存在的连接实例 - 检查
database.php中的'params' => [\PDO::ATTR_PERSISTENT => true]:开启持久连接后,TP 会尝试复用,但需配合 Web 服务器模型(如 PHP-FPM 的pm = static更有效;pm = dynamic下效果不稳定)
如何监控真实连接复用情况:看 PDO 实例 ID 和 MySQL 线程 ID 对应关系
不能只看 TP 日志里的 SQL 条数,要验证底层是否真复用了同一个连接。最直接的方式是在查询前后打印 PDO 实例哈希和 MySQL 线程 ID:
Db::query('SELECT CONNECTION_ID()');
var_dump(spl_object_hash(Db::getConnection()->getPdo()));
常见错误现象:同一请求中两次 spl_object_hash() 输出不同,说明连接被重建了——大概率是中间件、事件监听器或手动 Db::close() 干扰了连接生命周期。
立即学习“PHP免费学习笔记(深入)”;
使用场景:
- 调试高并发下连接泄漏:在
AppServiceProvider::register()中注册一个全局钩子,记录每个请求开始/结束时的活跃连接数 - 对比不同部署模式:Nginx + PHP-FPM(静态进程) vs Swoole(常驻内存),后者必须显式管理连接,否则
Db::connect()每次都返回新实例 -
'debug' => true开启后,TP 会在 trace 日志里记录connection_id,但仅限于当前请求上下文,无法跨请求追踪
Swoole 环境下必须手动接管连接生命周期,否则连接数指数级增长
ThinkPHP 的连接管理基于 PHP 请求生命周期,而 Swoole 是常驻内存模型。若不做处理,每个协程都会新建 PDO 连接,且不会自动销毁,直到 worker 进程重启。
实操建议:
- 禁用
'params' => [\PDO::ATTR_PERSISTENT => true]—— 在 Swoole 中持久连接反而导致连接句柄堆积,出现Too many connections - 改用连接池组件,例如
hyperf/pool或自建ConnectionPool类,通过make()/release()显式控制 - 在协程结束前强制关闭连接:
go(function () { Db::getConnection()->close(); });,但要注意:关闭后该连接实例不可再用,后续查询需重新Db::connect() - 避免在
__construct或initialize()中预创建连接,Swoole 下类实例会长期存在,连接也会长期占用
长期趋势分析只能靠外部采集:MySQL SHOW PROCESSLIST + 请求埋点
ThinkPHP 自身不提供连接复用率指标,也没有历史统计接口。想分析“复用率趋势”,必须跳出框架,从 MySQL 层和业务层双向采集数据。
性能与兼容性影响:
- 高频执行
SHOW PROCESSLIST本身有开销,建议每 30 秒采样一次,过滤掉Command = Sleep的空闲连接 - 在核心 API 入口统一埋点:记录每次请求的
Db::getConnection()调用次数、首次连接耗时、最大并发连接数 - 将数据写入时序数据库(如 Prometheus + Grafana),用
count by (app, host) (mysql_processlist_threads{state="Query"})观察连接活跃度变化 - 注意 MySQL 8.0+ 的
performance_schema.threads表比SHOW PROCESSLIST更准,但需要开启performance_schema
容易被忽略的一点:连接复用率高 ≠ 效率高。如果复用的是慢查询堆积的连接,或者事务未提交导致锁等待,复用反而放大阻塞。监控重点不该是“复用了多少次”,而是“平均每个连接承载了多少有效查询”和“连接空闲时间占比”。



















