php-fpm下无真正连接池,db::connection()多次调用复用同一pdo实例(单请求内),开启pdo::attr_persistent仅为进程级缓存,易致事务残留、连接失效等问题。

PHP-FPM 下没有连接池,所谓“复用”仅限单次请求内;强行开持久连接反而容易引发事务残留和数据错乱。
为什么 DB::connection() 多次调用不等于新建连接
在同一个 HTTP 请求生命周期中,DB::connection('mysql') 默认返回的是同一个 PDO 实例——只要没手动调用 DB::disconnect() 或 DB::purge()。你可以用 var_dump(DB::connection()->getPdo() === DB::connection()->getPdo()) 验证为 true。
这意味着:DB::table('users')->first() 和后续的 DB::table('posts')->where('user_id', 1)->get() 共享底层 TCP 连接,但这个“共享”不会跨请求延续。
- 不要在循环里反复写
DB::connection(),直接复用第一次获取的对象 - 避免在中间件或服务提供者中提前调用
DB::connection()并缓存,它只对当前请求有效 -
DB::reconnect()是断开再重连,不恢复事务上下文,高并发下会加剧抖动
PDO::ATTR_PERSISTENT = true 是什么、为什么一般别开
开启后,PHP-FPM worker 进程会在请求结束后保留底层 socket 连接,下次同 worker 处理请求时可跳过 TCP 握手和认证。但它不是连接池,而是“进程级连接缓存”。
副作用非常实际:SQLSTATE[HY000]: General error: 2006 MySQL server has gone away、临时表残留、用户变量污染、事务未提交却卡住连接——尤其在队列任务或长脚本中极易出问题。
- 默认就是
false,除非你明确压测过且能接受状态污染风险,否则别改 - 如果真要试,必须同步调低 MySQL 的
wait_timeout(建议 60–120 秒),否则空闲连接长期挂起,反而占满max_connections - 开启后务必禁用所有依赖连接状态的功能:如
SET @var := ...、CREATE TEMPORARY TABLE、未显式COMMIT的事务
Swoole / Octane 场景下才需要配 pool 参数
只有当你的 Laravel 已运行在 Swoole、RoadRunner 或 Laravel Octane 这类常驻内存环境中,'pool' => ['min_connections' => 5, 'max_connections' => 20] 才真正生效。此时每个 Worker 进程会维护一组可被协程调度复用的连接。
常见失效原因:
-
host写成localhost:Swoole 会走 Unix socket,绕过连接池逻辑;必须改成127.0.0.1 - 没在
config/database.php的 MySQL 配置块里加'options' => [PDO::ATTR_TIMEOUT => 5],导致某个慢查询卡死整个连接池 - 读写分离配置里漏掉
'read' => ['host' => '...']和'write' => ['host' => '...'],连接池只作用于主配置,从库连接不受控
MongoDB 是少数真支持连接池的场景
Laravel MongoDB 驱动基于官方 C 扩展,maxPoolSize 和 minPoolSize 是真实起效的配置项,不是占位符。
生产环境起步建议:
-
maxPoolSize => 15,minPoolSize => 5(按 CPU 核心数 × 2 调整) - 必须设
'persistConnection' => true,否则每次请求仍重建连接+认证 -
socketTimeoutMS设为30000,connectTimeoutMS设为10000,避免超时阻塞协程
注意:这些配置只对 mongodb driver 生效,MySQL/PostgreSQL 的同名字段在 Laravel 当前所有版本中均未被读取。











