pdo持久连接必须在new pdo()构造时通过options数组显式传入pdo::attr_persistent => true,且dsn、用户名、密码严格一致才可复用;它按php-fpm进程隔离,非跨进程连接池,需配合proxysql等中间件实现真正连接池化。

PDO驱动下必须显式传PDO::ATTR_PERSISTENT => true
PHP 8.0 的 PDO 默认不启用持久连接,哪怕你在 DSN 里写 persist=true 也无效。真正起作用的只有 params 数组里的这个键值对。常见错误是只配了 'persistent' => true 或把配置塞进 options,结果连接照样每次新建。
正确写法:
$pdo = new PDO(
'mysql:host=localhost;dbname=test;charset=utf8mb4',
'user',
'pass',
[
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]
);
-
PDO::ATTR_PERSISTENT必须为true(布尔值),字符串"true"会被转成false - 不要和
PDO::MYSQL_ATTR_INIT_COMMAND等其他属性混在同一个数组里却不加逗号——语法错误会直接报PDOException - 若用 ThinkPHP 8,需确认
'type' => 'pdo'已启用,且'params'是顶层键,不是嵌套在'connection'里
mysqli驱动用p:前缀主机名,别信pconnect配置项
mysqli 在 PHP 8.0+ 中已不支持 'pconnect' => true 这种配置方式。它只认一种形式:在 host 参数开头加 p:。比如 'p:localhost',否则就是普通连接。
示例:
$mysqli = new mysqli('p:localhost', 'user', 'pass', 'test');
- 如果用
mysqli_connect()函数,同样要写成mysqli_connect('p:localhost', ...) - 注意:PHP-FPM 下每个 worker 进程会维护自己的持久连接池,
p:开头的连接不会跨进程复用 - MySQL 服务端看到的是多个 Sleep 状态连接,不是单个长连接——这是正常现象,不代表泄漏
FPM环境下持久连接容易堆积Sleep连接
执行 SHOW PROCESSLIST 后发现大量 Command 列为 Sleep,说明连接没被主动释放,而是留在 FPM 子进程里等待复用。这不是并发高导致的,而是脚本异常中断、事务未提交、或用了阻塞 I/O(如 file_get_contents)让请求卡住。
- 检查代码里是否有未
commit或rollback的事务——持久连接会继承上次事务状态 - 避免在循环中反复调用
Db::table()->find(),合并为whereIn查询 - 生产环境建议默认关闭持久化:
PDO::ATTR_PERSISTENT => false,比修复泄漏更省事 - 如果必须开,务必配好 MySQL 的
wait_timeout(建议设为 60–120 秒),并让 PHP 层做连接探活
Swoole协程里别用PDO持久化,该换Swoole\Coroutine\MySQL
在 Swoole 常驻内存模式下,PDO 的 PDO::ATTR_PERSISTENT 不起作用——协程调度器自己管理连接生命周期,PDO 持久化反而干扰资源回收。TP8 ≥ 8.0.12 才支持协程 MySQL 客户端,前提是启动命令是 php think swoole,不是 php think run。
- 确认运行模式:
php --ri swoole查看是否启用了协程支持;php think swoole status看服务是否在协程模式运行 - TP8 配置里删掉所有
'pool' => true,它在 FPM 下被忽略,在 Swoole 下由框架自动接管 - 连接复用靠协程上下文,不是靠 PDO 层面的持久标记——写 SQL 时不用改,但不能再依赖
__destruct关闭连接
pm.max_children,却只给 MySQL 配了 30 个 max_connections,结果一半连接直接失败,还误以为是持久化配置错了。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











