mysqli持久连接必须在主机名前加p:前缀,如'p:127.0.0.1',端口须单独传第5参数,不可混入host字符串;PDO需设ATTR_PERSISTENT=true并配合健康检查;两者均受MySQL wait_timeout和认证插件影响,高并发下易引发连接数膨胀与状态残留风险。

mysqli 持久连接必须加 p: 前缀,不能只改 host
PHP 8.5.7 的 mysqli 不像旧版 mysql_connect 那样靠函数名区分持久连接,它完全依赖主机名前缀。写成 new mysqli('localhost', ...) 永远是普通连接;必须显式写成 new mysqli('p:localhost', ...) 或 new mysqli('p:127.0.0.1', ...) 才会启用连接池复用。
常见错误:把 host 写成 'localhost:3306' 或 '127.0.0.1:3306' —— 这会导致解析失败,报 Error while reading greeting packet;端口号必须单独传第 5 个参数 $port,不能塞进 host 字符串里。
- 正确写法:
new mysqli('p:127.0.0.1', $user, $pass, $db, 3306) - 错误写法:
new mysqli('p:127.0.0.1:3306', ...)(端口混入 host) - 本地 Unix socket 场景下不支持持久连接,
p:localhost会退化为普通连接
PDO 没有原生持久连接,靠 ATTR_PERSISTENT 且需配合健康检查
PDO 的持久连接只是语义上的“复用”,底层仍依赖 MySQL 服务端的 wait_timeout 控制空闲断连。开启方式是设置选项 PDO::ATTR_PERSISTENT => true,但仅此一项远远不够。
PHP 8.5.7 下,若不主动检测连接状态,复用一个已失效的持久连接会直接触发 MySQL server has gone away。这不是 PDO 的 bug,而是 TCP 层自然断连的结果。
立即学习“PHP免费学习笔记(深入)”;
- 必须在每次 query 前加健康检查,例如执行
$pdo->query("SELECT 1")->fetch()或更轻量的$pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS) - 不要在 long-running 脚本(如 Laravel queue worker)中无条件复用同一个
$pdo实例 - 若发现连接异常,直接
unset($pdo)再new PDO(...)重建,比修复更可靠
持久连接不是万能加速器,高并发下反而可能拖慢响应
MySQL 默认 max_connections = 151,而每个持久连接会常驻占用一个 slot。当并发请求量接近这个值时,新请求会被阻塞在连接队列,表现为整体响应变慢、超时增多,甚至触发 Too many connections 错误。
更隐蔽的问题是状态残留:虽然 mysqli 自动调用 mysql_change_user() 清理事务/锁/变量,但某些自定义会话变量、临时表或未提交的读写操作仍可能污染后续请求。
- 建议监控
SHOW STATUS LIKE 'Threads_connected',持续高于 100 就要警惕 - Web 场景下,单次请求生命周期短,普通连接开销远小于维护持久连接的复杂度
- 真正适合持久连接的场景是 CLI 长任务(如定时同步脚本),且需确保每次循环后手动清理上下文
MySQL 8.0+ 用户权限与认证插件会静默破坏持久连接复用
MySQL 8.0 默认使用 caching_sha2_password 插件,而 PHP 8.5.7 的 mysqli 和 PDO 在复用持久连接时,不会重新协商认证方式。如果首次连接用的是 mysql_native_password,后续复用却遇到插件变更,就会卡在握手阶段,表现为连接缓慢或静默失败。
这个问题在非交互式复用中极难定位,因为第一次连接成功了,错误只在第二次复用时浮现。
- 最稳方案:登录 MySQL 执行
ALTER USER 'your_user'@'%' IDENTIFIED WITH mysql_native_password BY 'xxx'; FLUSH PRIVILEGES; - 不推荐改全局默认插件,会影响其他客户端
- 若必须用
caching_sha2_password,请确认 PHP 编译时链接的是 MySQL Native Driver(mysqlnd),而非 libmysql
持久连接真正的价值不在“省一次 connect”,而在避免反复 TLS 握手或云数据库的连接建立延迟。但 PHP 8.5.7 下,它带来的运维复杂度和隐性风险,往往超过收益——除非你清楚知道每条连接的生命周期、MySQL 的线程模型,以及自己代码里有没有残留未关闭的事务或锁。



















