ThinkPHP 8 数据库连接数过多的根本原因是连接未释放、误用持久化或在FPM下错误配置'pool'=>true;TP8无原生连接池,该配置仅在Swoole协程(php think swoole且TP≥8.0.12)中生效,FPM下Db::connect()每次新建PDO连接,异常或阻塞操作易致Sleep连接堆积。

ThinkPHP 8 数据库连接数过多,不是改个 max_connections 就能解决的。根本问题通常出在连接没释放、误用持久化、或在非协程环境强行套用“连接池”配置——这些操作不仅无效,还会让连接堆积更严重。
Db::connect() 在 FPM 下每次都是新连接,别信 'pool' => true
很多人在 database.php 里加了 'pool' => ['enable' => true],压测时发现连接数照涨不误。这是因为 TP8 本身不提供连接池实现,'pool' 配置项在 FPM 模式下完全被忽略。Db::connect() 每次都会 new 一个 PDO 实例,请求结束才由 PHP 自动 close——但若中间抛异常、或用了 file_get_contents() 等阻塞操作,连接就卡在 Sleep 状态不释放。
- 确认当前运行模式:
php think run是 FPM,php think swoole才是协程 - FPM 下删掉所有
'pool'相关配置,它不起作用 - 用
SHOW PROCESSLIST查看Command列,大量Sleep表示连接空闲未关闭,不是并发高,是脚本没走完 - 循环中避免反复调用
Db::table()->find(),合并查询或用whereIn批量拉取
mysqli 驱动可开 pconnect,PDO 的持久化要手动传参
TP8 默认用 PDO,但 'persistent' => true 这种写法在 PDO 驱动里无效。PDO 的持久连接必须通过 params 显式传入 PDO::ATTR_PERSISTENT => true,否则配置项被无视。
- mysqli 驱动下,
'pconnect' => true是有效配置,但仅限于 Apache/mod_php 或旧版 PHP-FPM(PHP 7.4 及以下),PHP 8+ 中风险更高 - PDO 驱动下,正确写法是:
'params' => [PDO::ATTR_PERSISTENT => true],且必须配合'type' => 'pdo' - 持久连接在 FPM 下极易导致连接泄漏:子进程退出时不真正断开,而是归还给 CGI 层连接池,MySQL 看到的是大量
Sleep连接 - 生产环境建议默认关闭持久化,用
'params' => [PDO::ATTR_PERSISTENT => false]显式禁用
真要用连接池,必须跑在 Swoole 协程里,且 TP ≥ 8.0.12
只有在 Swoole 协程服务器中,TP8 才会把 Db::table() 路由到协程 MySQL 客户端(如 Swoole\Coroutine\MySQL),此时连接才能复用。但前提是版本够新、驱动配对、且调用方式合规。
PHP 8.5.12 RC 1 候选版修复 BZ2、DOM、PDO、Opcache/JIT 和扩展内存安全问题,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
立即学习“PHP免费学习笔记(深入)”;
- 启动命令必须是
php think swoole,不是php think run - TP 版本不能低于 8.0.12,早期 8.0.x 存在协程适配 bug,连接不归还
- 数据库配置中
'type'必须明确设为'mysql'(不是'pdo'),否则协程客户端初始化失败,退化为普通 PDO - 事务内连接不会进池:
Db::transaction()强制绑定当前协程独占连接,结束后销毁;若事务跨协程或中途 yield,连接直接丢失 - 心跳必须开:
'options' => ['heartbeat' => 3],不然 MySQLwait_timeout=8秒后 kill 连接,下次复用报SQLSTATE[HY000] [2006] MySQL server has gone away
查连接不能只看总数,得盯住 Time > 60 和 Info 字段
连接数高 ≠ 性能差。可能是 500 个连接里 490 个是空闲 Sleep,10 个正在执行慢查询。光调大 max_connections 只会让问题更隐蔽。
- 执行
SHOW PROCESSLIST,优先过滤Time > 60的记录,再看Info字段是否是SELECT ...或UPDATE ...,还是空着(表示已卡住) - 同步打开 MySQL 慢日志:
SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; - TP 日志里的
[ SQL ]行要和Processlist时间戳对齐,确认是不是同一请求拖住连接 -
wait_timeout和max_idle_time必须匹配:比如池设max_idle_time = 300,MySQL 就得SET GLOBAL wait_timeout = 300,否则连接被服务端先干掉
最常被跳过的动作是:没确认运行模式就配池,没检查慢日志就调参数,没看 Processlist 的 Time 列就杀连接。连接数问题本质是生命周期管理问题,不是数字问题。


















