thinkphp 8.0 本身无原生数据库连接池,仅在 swoole 协程环境下通过 think-swoole 扩展实现;配置必须写在 config/swoole.php 的 database 节点且 enable=true,db::table() 默认不走池,须显式调用 db::pool(),并确保 mysql、swoole worker 数与协程并发量三层参数对齐。

ThinkPHP 8.0 本身没有数据库连接池——你配了 pool 字段、改了 max_connections、甚至重启了服务,只要没跑在 Swoole 协程环境下,连接池就根本不存在。
config/swoole.php 才是唯一生效的配置入口
TP8 的 database.php 里加任何 pool、min_connections、wait_timeout 都会被忽略。连接池能力由 topthink/think-swoole 扩展提供,配置必须写在 config/swoole.php 的 database 节点下:
-
enable必须设为true,否则整个节点被跳过,仍走短连接 -
min_connections是 Worker 启动时预创建的连接数,设太低会导致首请求延迟;设太高可能直接触发 MySQL 的max_connections限制 -
max_connections是单个 Worker 进程内池子的总连接上限(空闲 + 忙碌),不是全局值;它和swoole.server.worker_num共同决定最大可用连接数 -
wait_timeout单位是秒,协程等待空闲连接超时后抛ConnectionPoolTimeoutException,不是毫秒
Db::pool() 必须显式调用,Db::table() 默认不走池
这是最常踩的坑:配完池,压测时 Too many connections 照样报。因为 Db::table('user')->select() 底层始终调用 Db::connect(),绕过池逻辑。
- ✅ 正确用法:
Db::pool()->table('user')->select() - ✅ 事务也得走池:
Db::pool()->transaction(function () { ... }) - ❌ 模型类中
self::where()->find()不走池,底层仍是Db::connect() - ❌
Db::transaction()默认使用独立非池化连接,且事务结束后连接不归还,而是销毁
三层限制不对齐,池再大也没用
连接池只是其中一环,MySQL、Swoole、协程三侧参数必须对齐,否则 QPS 上不去、连接照样爆满:
- MySQL 侧:
SHOW VARIABLES LIKE 'max_connections';必须 ≥max_connections × swoole.server.worker_num - Swoole 侧:
swoole.server.worker_num决定最多几个进程能并发取连接;设成 2,池里有 100 个连接也只用得上 2 个 - 协程侧:一个 Worker 内启了 200 个协程查库,但池子只有 8 个连接,剩下 192 个协程全在排队等
wait_timeout - 验证是否真复用:同一协程内连续两次调
Db::pool()->getPdo(),打印spl_object_id(),相同才说明复用成功
心跳和 wait_timeout 必须协同调大
MySQL 默认 wait_timeout=8 秒,闲置连接会被服务端主动断开。协程下次复用时会抛 SQLSTATE[HY000] [2006] MySQL server has gone away。
- 在
config/swoole.php的database节点加'heartbeat' => 30,表示每 30 秒发一次 PING - MySQL 侧同步执行:
SET GLOBAL wait_timeout = 300;(需 SUPER 权限) -
wait_timeout(池内等待空闲连接超时)和 MySQL 的wait_timeout是两回事,别混淆;前者建议设为 2–5 秒,后者建议 ≥ 300 秒 - 别在
database.php里设'break_reconnect' => true,协程下它会干扰自动重连逻辑
真正难的不是配参数,而是在整个代码链路里确保所有数据库操作都经过 Db::pool() —— 中间件、模型事件、定时任务、异常日志写入,任何一处漏掉,都可能悄悄新建连接并长期滞留。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











