ThinkPHP数据库连接不复用的根本原因是其默认未启用真正的连接池,而是使用PDO长连接,该连接仅在单次请求内有效,PHP-FPM进程重启、超时或空闲回收均会导致连接断开。

为什么 thinkphp 的数据库连接看起来建了却总不复用?
根本原因不是连接池没开,而是 ThinkPHP 默认压根没连上真正的连接池——它用的是 PDO 的长连接(PDO::ATTR_PERSISTENT => true),但这个“持久”只在单次请求内有效,PHP-FPM 进程重启、连接超时、空闲回收都会让连接断掉。你看到的“连接数涨得快”,其实是每次请求都新建连接,又没被复用,而不是连接池失效。
常见错误现象:SHOW PROCESSLIST 里大量 Sleep 状态连接,但应用日志里查不到对应请求;监控显示连接数峰值高,但 QPS 并不高;mysqladmin extended-status | grep Threads_connected 持续攀升。
- ThinkPHP 6.x 默认关闭连接池,
'pool' => []是空配置,等于没启用 - 即使配了
'pool' => ['min' => 5, 'max' => 20],底层仍依赖 Swoole 或 Hyperf 提供的协程连接池,原生 PHP-FPM 环境下该配置完全不生效 -
Db::connect()每次调用默认走新连接,除非显式传入已存在的连接实例或开启事务上下文
怎么确认当前是否真用了连接池?
别信配置文件,要看运行时行为。最直接的办法是打日志或调试连接对象的生命周期。
- 在
app/common/Db.php或数据库中间件里加一行:Log::info('db connection id: ' . spl_object_id($connection)); - 发起两次快速请求(同一 FPM worker),如果两次日志里的
spl_object_id不同,说明没复用;相同且连接未 close,则大概率进了连接池(前提是用了 Swoole 协程) - 检查
config/database.php中'type' => 'mysql'下是否有'pool' => ['enable' => true]—— 这个字段在官方文档里存在感极低,但必须显式设为true才会触发连接池初始化逻辑 - 运行
php think swoole启动后,执行lsof -i :3306 | wc -l,对比 FPM 模式下的连接数,差值大才说明池起了作用
Db::table()->select() 为什么每次都不走池里已有连接?
因为 ThinkPHP 的查询构造器默认不绑定连接上下文,每次调用都可能触发新连接获取逻辑,尤其在多库、动态连接、闭包回调等场景下更明显。
立即学习“PHP免费学习笔记(深入)”;
- 避免在循环里写
Db::table('user')->where(...)->find(),改用批量查询Db::table('user')->whereIn('id', $ids)->select() - 手动复用连接:先
$conn = Db::connect('mysql'),再$conn->table('user')->select(),确保同一请求内多次操作共享一个连接对象 - 事务内自动复用:只要在
Db::transaction(function () { ... })里执行的查询,都会复用同一个连接,这是唯一开箱即用的复用路径 - 注意
Db::name()和Db::table()底层行为不同:前者会走模型连接缓存,后者更轻量但也更“干净”,容易绕过缓存
告警阈值怎么设才不会误报?
连接复用率低 ≠ 有问题,关键看单位时间内的连接创建频次和平均存活时长。直接监控 Threads_created 比 Threads_connected 更准。
- 在 MySQL 中执行:
SHOW GLOBAL STATUS LIKE 'Threads_created';,每分钟采集一次,增长 > 10 次/分钟且持续 5 分钟,才值得告警 - 配合 PHP 应用层埋点:在
think\db\Connection::__construct()里加计数器(需重写或 AOP),统计每分钟 new 出的连接数 - 不要按“连接数 > 50 就告警”,FPM 下 100 个 worker + 慢查询积压,
Threads_connected到 200 是常态;重点看Threads_created / second是否远高于 QPS - 忽略
wait_timeout和interactive_timeout的干扰:这两个值设太小会导致连接频繁重建,建议调到 300 秒以上再观察复用率
pool 配置,其实连池的边都没摸到。



















