FrankenPHP Worker模式下数据库连接不自动持久,因PHP不接管连接生命周期,需在onWorkerStart手动初始化并管理;PDO持久连接有复用陷阱,须配一致DSN、设合理wait_timeout、加ping检测,或改用ProxySQL/Swoole连接池。

要自己管。FrankenPHP Worker 模式下,PHP 进程常驻内存,但数据库连接不会自动复用或保活,PDO 或 mysqli 的默认行为依然是一次请求一建连、脚本结束即关闭——除非你显式干预。
Worker 模式下数据库连接不自动持久的原因
FrankenPHP 的 Worker 模式让 PHP 代码常驻,但它并不接管你的数据库连接生命周期。它只保证 onWorkerStart(类比)阶段的代码只执行一次,而 onRequest(实际是每个 HTTP 请求触发的 handler)仍按需运行。这意味着:
- 如果你在每个请求里 new PDO(),就会重复建连,和 FPM 下一样;
- PDO 的
PDO::ATTR_PERSISTENT => true在 FrankenPHP 中**有效但有陷阱**:它依赖 PHP 内部连接池,但仅当 DSN、用户名、密码、socket 路径等**完全一致**时才复用,差一个charset=utf8mb4就新建一条; - MySQL 的
wait_timeout和 FrankenPHP Worker 进程存活时间不对齐时,连接可能在复用前就已被服务端断开,下次使用直接报MySQL server has gone away。
必须手动初始化并管理连接池
推荐在 onWorkerStart 阶段(即 Worker 初始化时)创建连接,并挂到全局静态变量或依赖容器中,供后续所有请求复用:
// caddyfile 启用 worker 模式后,PHP 入口文件中
class DatabasePool
{
private static ?PDO $instance = null;
public static function getConnection(): PDO
{
if (self::$instance === null) {
self::$instance = new PDO(
'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4',
'root',
'123456',
[
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
}
return self::$instance;
}
}
注意三点:
立即学习“PHP免费学习笔记(深入)”;
- 不要在
onRequest或路由 handler 中 new PDO; - 务必检查 MySQL 端
wait_timeout(建议设为 300–600),并在连接复用前加简单 ping($pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS)或执行SELECT 1); - 如果用了事务,必须在每次请求结束前显式
commit或rollback,否则连接会卡在事务状态,影响复用。
比持久连接更稳的是连接池中间件
纯 PDO 持久连接在高并发下仍可能因异常断连、状态残留导致连接泄漏。生产环境建议把数据库连接交给独立连接池管理:
- 用 ProxySQL:配置
mysql-pool_enabled+ 调大mysql-default_max_connections_per_hostgroup,PHP 连接地址指向 ProxySQL(如host=127.0.0.1;port=6033); - 用 Swoole 协程池(即使不用 Swoole Server):通过
Swoole\Coroutine\MySQL或co\run()+new Swoole\Coroutine\MySQL实现真正的异步复用; - 避免混用:FrankenPHP Worker + PHP-FPM 子进程共存时,两套持久连接会叠加占用
max_connections,极易触发Too many connections。
最易被忽略的一点:FrankenPHP 的 Worker 进程不会像 Workerman 那样提供 onClose 或 onWorkerStop 钩子来优雅释放连接。一旦进程被信号终止(比如 kill -15),你挂在静态变量里的 PDO 实例会被直接销毁,底层 TCP 连接由操作系统回收——这没问题;但若进程因 OOM 或 panic 崩溃,连接可能滞留 MySQL 端,需要靠 wait_timeout 清理。所以监控 Threads_connected 和 frankenphp_busy_threads 必须同步做。



















