不能直接用 new PDO(),因为FrankenPHP Worker模式下PDO实例长期复用,不启用持久连接会导致连接泄漏、事务残留、“server has gone away”等错误;必须显式配置PDO::ATTR_PERSISTENT => true,并禁用自动关闭、配对释放手动获取的连接。

FrankenPHP独立应用必须显式启用PDO持久连接,否则连接会泄漏、事务残留、MySQL报错“server has gone away”
为什么不能直接用 new PDO()?
FrankenPHP Worker 模式下,PHP进程长期存活,PDO 实例不会随请求销毁。若仍沿用传统“每次请求 new PDO()”,会导致:
- 连接数静默上涨,
netstat -an | grep :3306 | wc -l可观察到缓慢攀升 - 事务未显式
commit或rollback,下次请求复用该连接时状态污染 - prepare 语句缓存跨请求混用,出现
SQLSTATE[HY000]: General error类错误 - MySQL 报
Too many connections,但日志里无明显堆积痕迹
如何正确配置 PDO 持久连接?
关键不是“能不能连上”,而是“连上后是否安全复用”。必须同时满足以下三点:
- 在 DSN 后传入
PDO::ATTR_PERSISTENT => true,例如:$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass, [PDO::ATTR_PERSISTENT => true]); - 禁用框架自动关闭逻辑:Laravel 需确认
doctrine.dbal.auto_close为false;ThinkPHP 在config/database.php中设'persistent' => true - 避免手动调用
DB::connection()->getPdo()后不unset—— 这类操作必须配对释放,否则连接引用一直存在
MySQL 服务端怎么配合?
Worker 进程复用连接,wait_timeout 基本失效,不能依赖它回收空闲连接:
立即学习“PHP免费学习笔记(深入)”;
- 在
my.cnf中显式设置:max_connections = 64
(起步值,按num_threads × 连接池大小反推) - 例如 FrankenPHP
num_threads = 8,每个线程最多持 5 个连接,则至少要设max_connections = 48 - 禁用
skip-name-resolve,避免 DNS 查询阻塞连接建立 - 不要在
init_connect里执行耗时 SQL,Worker 启动时会批量初始化连接
连接泄漏怎么快速定位?
最直接的信号是连接数缓慢但持续增长,且与请求量不严格成正比:
- 用
show processlist查看当前活跃连接,注意Command列是否大量为Sleep且Time超过 10 秒 - 检查是否有
new PDO()出现在循环或高频方法中,且没包在try/finally里做$pdo = null - Doctrine Proxy 类或 classmap autoload 多的项目,实际连接开销可能比理论高 20%~30%,压测时需预留余量
真正麻烦的不是连不上,而是连上了却没人管——连接、事务、prepare 缓存都在进程里静默滞留。Worker 模式下,数据库连接不再是个“请求生命周期内”的临时资源,而成了需要主动管理的常驻状态。



















