ThinkPHP 官方无连接池实现,FPM 模式下配置 pool 无效;所谓“复用”仅指单请求内共用 PDO 实例,需避免重复调用 Db::connect();持久连接(PDO::ATTR_PERSISTENT)风险高,慎用。

ThinkPHP 连接池不存在,别配 pool 了
ThinkPHP 官方底层没有连接池实现,所谓“配置连接池”是常见误解。它的 database.php 中的 pool 配置项(如 'pool' => ['max' => 20])在 6.x 版本中仅对 Swoole 或 Workerman 等协程/常驻进程环境生效,且需手动集成第三方组件(如 topthink/think-swoole),标准 FPM 模式下完全不生效。
常见错误现象:mysql has gone away、短时间高并发时连接数暴涨、Too many connections 报错——这些都不是靠开“池子”能解决的。
- FPM 下每次请求都是全新 PDO 实例,连接生命周期 = 请求周期,不存在复用一说
- 想让连接“复用”,本质是减少新建连接次数,不是维持长连接
- 若强行在 FPM 中配置
'deploy' => 1或'persistent' => true,反而可能因连接未正确释放导致状态污染(比如事务未回滚、字符集残留)
pdo::ATTR_PERSISTENT 在 ThinkPHP 里要慎开
PHP 原生支持持久连接,通过 PDO::ATTR_PERSISTENT => true 实现。ThinkPHP 允许在数据库配置中传入 'params' => [PDO::ATTR_PERSISTENT => true],但效果高度依赖 SAPI 和 MySQL 服务端配置。
使用场景有限:仅适用于稳定、低频变更、无跨请求状态依赖的简单读操作;一旦涉及事务、临时表、用户变量,极易出错。
立即学习“PHP免费学习笔记(深入)”;
- MySQL 服务端必须开启
wait_timeout足够长(建议 ≥ 300),否则连接被服务端主动断开后,PDO 不会自动重连,下次查询直接报MySQL server has gone away - FPM 子进程重启(如 reload、OOM kill)会导致所有持久连接丢失,但 PHP 不通知应用层,后续查询可能卡死或返回旧数据
- ThinkPHP 的
lastInsertId()、getPdo()->inTransaction()等方法在持久连接下行为不可靠
真正有效的复用:用 Db::connect() 复用连接实例
ThinkPHP 的连接复用,实际是指“同一请求内多次查询共用一个 PDO 实例”,这是默认行为,无需额外配置。但开发者常误写成多次调用 Db::connect('mysql2') 或重复 new Connection,导致隐式新建连接。
关键点在于:连接对象应被缓存、复用,而不是反复初始化。
- 避免在循环里写
Db::connect('log_db')->table('log')->insert($item)—— 每次都重建连接对象(虽底层 PDO 可能复用,但框架层开销白费) - 正确做法是提前获取一次连接对象:
$logDb = Db::connect('log_db');,后续所有操作都基于它 - 若需跨模型复用,可将连接对象绑定到容器:
Container::getInstance()->bind('log_db', function () { return Db::connect('log_db'); });,再通过app('log_db')获取 - 注意:不同数据库配置名(如
'mysql'和'mysql_slave')对应不同连接实例,无法混用
长连接 ≠ 连接复用,FPM 下该关就关
所谓“长连接优化”,在传统 PHP-FPM 架构里是个伪需求。连接保持打开状态超过请求周期,只会占用 MySQL 线程、拖慢服务端资源回收,还可能因网络中断变成僵尸连接。
真正该做的是:让连接“快建、快用、快放”。ThinkPHP 默认已做到这点——查询结束即释放 PDO 引用,PHP 请求结束自动析构。
- 检查 MySQL 的
max_connections是否合理(建议设为峰值并发 × 1.5,而非盲目调大) - 确认没在全局中间件或
AppService中提前调用Db::connect()并长期持有引用(会导致连接无法及时释放) - 如果用了
Db::transaction(),务必确保try/catch/finally结构完整,避免事务未提交/回滚导致连接挂起
复杂点在于:你以为在复用连接,其实只是没搞清请求生命周期;你以为配了长连接,其实只是让 MySQL 多等了几秒空闲连接。该断的时候,就得断干净。


















