PHP 8.3+持久连接仅进程级复用,非真正连接池;必须在new PDO()或mysqli_connect()初始化时通过PDO::ATTR_PERSISTENT=>true或p:前缀声明,DSN/用户/密码须完全一致,且受限于PHP-FPM worker数与MySQL max_connections配比。

PHP最新版(8.3+)建立的“持久连接”仍是进程级复用,不是真正的连接池;它只在同一个PHP-FPM worker生命周期内有效,且必须在new PDO()或mysqli_connect()初始化时就声明,否则无效。
PDO::ATTR_PERSISTENT必须在构造时传入
很多开发者误以为可以在连接创建后用setAttribute()开启持久化——这完全没用。PDO驱动只在实例化阶段读取PDO::ATTR_PERSISTENT,后续调用$pdo->setAttribute(PDO::ATTR_PERSISTENT, true)不会生效。
- 正确写法:
$pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_PERSISTENT => true]); - DSN、用户名、密码三者必须完全一致,否则无法命中已有连接(比如
localhost和127.0.0.1被视为不同主机) - 不要混用
PDO::ATTR_PERSISTENT => true和PDO::ATTR_ERRMODE => PDO::ERRMODE_SILENT,后者会让连接失效时静默失败,掩盖问题
mysqli用p:前缀,但自动清理有开销
mysqli不支持选项数组传参启用持久化,必须在主机名前加p:前缀,例如mysqli_connect('p:localhost', $user, $pass)。它会在连接归还池时自动执行mysql_change_user()做状态清理(回滚事务、释放锁、重置变量等),这是PDO不具备的安全保障。
- 优点:避免残留锁或未提交事务污染后续请求
- 缺点:每次复用都触发一次用户切换,比纯TCP复用略慢;高并发下可考虑编译时加
MYSQLI_NO_CHANGE_USER_ON_PCONNECT禁用(需自行确保清理) - 注意:
p:127.0.0.1和p:localhost仍算不同连接,DNS解析差异会导致连接池分裂
PHP-FPM worker数与MySQL max_connections必须对齐
每个PHP-FPM worker最多持有一个持久连接,若pm.max_children = 200而MySQL的max_connections = 150,超出的50个worker会阻塞在连接等待上,最终超时失败。
- MySQL侧建议设
wait_timeout = 300(略大于PHP脚本最长执行时间),避免连接被服务端单方面断开 - PHP-FPM侧应设
pm.max_requests = 500左右,让worker定期重启,防止长期运行导致内存泄漏或连接老化 - 别依赖
mysql_pconnect()——该函数在PHP 7.0+已被移除,仅存于废弃的mysql扩展中
持久连接不是万能解药,高并发场景慎用
持久连接省的是TCP握手和认证开销,但解决不了慢查询、锁竞争或连接状态残留问题。更麻烦的是:它无法跨worker共享,也无法保证事务延续性——你在一个请求里开启事务,下一个请求复用同一连接时事务早已回滚或失效。
- 临时表、会话变量、
SET NAMES等状态可能残留,PDO完全不清理,mysqli虽清理但不覆盖所有场景 - 命令行脚本(如crontab)或Swoole协程环境里,持久连接基本无效,因为进程/协程模型不同
- 真正需要连接池管理的业务(如微服务间高频调用),应改用ProxySQL或PgBouncer这类外部中间件
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











