Laravel每次请求新建数据库连接是因为PHP-FPM进程模型导致连接无法跨请求复用,即使启用PDO持久连接也需谨慎配置,否则易引发状态污染;更可靠的方案是引入ProxySQL等连接池代理。

为什么 Laravel 每次请求都在新建数据库连接
Laravel 默认用 PDO + 连接池(connection pool)机制,但「复用」只发生在单次请求生命周期内;一旦 PHP-FPM worker 处理完一个请求,连接就会被关闭(除非启用了 PDO::ATTR_PERSISTENT)。很多人误以为开了 DB::connection() 就能跨请求复用,其实不能——PHP 的进程模型决定了每次请求都是全新上下文。
常见错误现象:Too many connections 报错、慢查询日志里大量 Connect / Quit 操作、监控显示连接数随 QPS 线性上涨。
- 使用场景:高并发 API 服务、短生命周期命令行任务(如队列消费者)、容器化部署(每个 pod 启动新 worker)
-
config/database.php中的'persistent' => true只对当前连接生效,且需驱动支持(MySQLi 不支持,PDO 支持但有副作用) - 开启持久连接后,连接不会自动清理事务状态、用户变量、临时表,可能引发数据污染
如何安全启用 PDO 持久连接
不是加个 'persistent' => true 就完事。Laravel 的 ConnectionFactory 在创建连接时会忽略这个选项,除非你绕过它手动配置。
实操建议:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 在
config/database.php的 MySQL 配置块中显式添加:'options' => [PDO::ATTR_PERSISTENT => true] - 必须同时设置
'strict' => false,否则某些 strict mode 下的持久连接会因 session 变量残留而报错 - 禁用
DB::reconnect()类操作——它会强行断开并重建连接,直接废掉持久化效果 - 验证是否生效:在 MySQL 侧执行
SHOW PROCESSLIST,观察相同 host:port 的连接是否长时间存在(注意区分 connection ID)
连接复用失效的三个典型陷阱
即使开了持久连接,实际运行中仍可能退化为短连接,原因往往藏在框架行为或业务代码里。
- 调用了
DB::purge('mysql')或DB::disconnect('mysql')—— 这会主动清空连接池,下一次DB::connection()必然新建 - 使用了
DB::transaction()但没正确结束(比如 throw 了未捕获异常),导致连接卡在事务中,被连接池标记为「不可复用」 - 在 Swoole / RoadRunner 等长生命周期环境中,未配置
'pool' => ['min_connections' => 5, 'max_connections' => 20],连接池初始化不足,高峰时仍频繁新建
比持久连接更稳的方案:连接池代理(如 ProxySQL)
PHP 层面的持久连接受制于 FPM worker 数量、超时配置、状态残留,越想「省连接」越容易出问题。真正降低 MySQL 连接压力,靠的是把连接管理从应用层移出去。
推荐做法:
- 在应用和 MySQL 之间加一层 ProxySQL,配置
mysql-pool,让所有请求复用 ProxySQL 自己维护的连接池 - Laravel 配置里把 host 改成
proxysql:6033,其他不变,完全无侵入 - ProxySQL 支持自动剔除故障连接、读写分离、连接数限制,比 PHP 自己硬扛可靠得多
- 注意:不要在 Laravel 中再开
persistent,避免双重连接池导致资源浪费
复杂点在于连接池代理引入了新运维面,但比起调试 MySQL server has gone away 和连接泄漏,这步迟早要走。尤其当你的 max_connections 已经调到 2000 还不够用时,说明问题不在 Laravel 怎么写,而在连接不该由它管。

















