PHP 怎么PHP 持久连接

wen PHP项目 2

本文目录导读:

PHP 怎么PHP 持久连接

  1. 常见数据库的持久连接实现
  2. PHP 持久连接的底层原理
  3. 何时使用持久连接?
  4. 常见坑与解决方案
  5. 持久连接的真实性能收益

在 PHP 中,持久连接(Persistent Connection)主要应用于数据库连接场景,它的核心思想是:在一次请求结束后,不关闭与数据库的连接,而是将其放入连接池中,供后续请求复用,从而避免每次请求都重新建立 TCP 连接的开销。

PHP 的持久连接机制与 Java 或 Python 等常驻内存的语言不同——PHP 本身是“请求-响应”模式,每个请求结束后内存会释放,PHP 的持久连接通常由扩展内部通过进程池或连接池实现。


常见数据库的持久连接实现

1 MySQL(mysqli / PDO)

mysqli 示例:

// 在主机名前加 "p:" 前缀
$db = new mysqli('p:localhost', 'user', 'pass', 'dbname');

PDO 示例:

$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::ATTR_PERSISTENT => true
]);

核心逻辑:

  • 连接关闭时不会真正断开,而是返回连接池。
  • 同一个 host:port:user 的后续请求会复用该连接。
  • 连接失效时(如 MySQL 超时关闭),扩展会自动清理。

⚠️ 注意事项:

  • 不要混用持久连接和非持久连接(可能导致连接泄漏)。
  • 使用持久连接时,必须显式处理事务回滚,否则残留事务会影响下一个请求。
  • PHP-FPM 模式下,持久连接会关联到进程:同一 worker 进程的多个请求可复用连接。

2 PostgreSQL(pgsql / PDO_PGSQL)

pgsql 扩展:

$conn = pg_pconnect("host=localhost dbname=test user=user password=pass");

或使用 pg_connectPGSQL_CONNECT_FORCE_NEW 常量。

PDO_PGSQL:

$db = new PDO('pgsql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::ATTR_PERSISTENT => true
]);

3 Redis / Memcached(非数据库持久连接)

  • Redisphpredis 扩展支持 pconnect() 方法,连接在进程生命周期内保持。
  • Memcached:构造函数中指定 persistent_id 实现复用。

PHP 持久连接的底层原理

PHP 本身不是常驻内存的,但 PHP 扩展(如 mysqli、pdo_mysql)可以在内部维护连接池,当启用 p:PDO::ATTR_PERSISTENT 时:

  1. 请求开始 → 检查连接池中是否有匹配的空闲连接(匹配条件:主机、端口、用户名、数据库名)。
  2. 有连接 → 复用(但需验证连接是否有效,无效则重建)。
  3. 无连接 → 创建新连接并加入池。
  4. 请求结束 → 不调用 mysql_close()pg_close(),而是将连接标记为空闲,放回池中。
  5. 下次同一进程的请求 → 从池中取出连接(跳过 TCP 握手)。

注意:连接池属于 PHP 进程级别,而非跨进程共享。

  • 使用 mod_php(Apache 模块)时,连接池随 Apache 子进程存活。
  • 使用 PHP-FPM 时,连接池随 FPM worker 进程存活。

何时使用持久连接?

✅ 推荐场景

  • 高并发、短连接频繁建立(如大量 API 请求)。
  • 数据库连接开销大(如使用 SSL、高延迟网络)。
  • PHP-FPM 工作进程数相对稳定(如 50~200 个进程)。

❌ 不建议的场景

  • 每个用户使用不同数据库账号(连接池无法复用)。
  • 数据库有连接数限制(持久连接会占用更多连接)。
  • 使用事务时未显式回滚(残留锁或事务影响后续请求)。
  • 使用长连接池管理工具(如 pgbouncer、ProxySQL) 时,应用层持久连接可能无意义。

常见坑与解决方案

1 连接死锁或断开

  • 现象:MySQL wait_timeoutinteractive_timeout 后,PHP 进程尝试复用已断开的连接。
  • 解决:启用 PDO::ATTR_PERSISTENT 时,PDO 内部会执行 SET AUTOCOMMIT=1ping() 检测,无效则重建。

2 事务残留

  • 问题:上一个请求未提交事务,下一个请求继续使用该连接,导致脏数据。
  • 解决:在请求结束时主动回滚事务。

3 连接数膨胀

  • 问题:每个 PHP-FPM worker 进程都持有独立连接,总连接数 = 进程数 × 每进程连接数。
  • 解决:限制 FPM pm.max_children,或使用连接池中间件(如 ProxySQL)。

持久连接的真实性能收益

场景 非持久连接 持久连接 收益
每次请求只执行一次简单查询 ~2ms 建立 + 0.1ms 执行 1ms 执行 显著
每次请求执行 10 次复杂查询 2ms 建立 + 50ms 执行 0ms 建立 + 50ms 执行 4%
数据库在本地(Unix Socket) 1ms 建立 0ms 建立 可忽略

当网络延迟较高(如云服务器跨可用区、外网数据库)或查询极短时,收益最明显。


  • 持久连接 ≠ 连接池:PHP 持久连接本质是进程级连接复用,而非真正跨进程的共享连接池。
  • 使用方式:加 p: 前缀(mysqli)或 PDO::ATTR_PERSISTENT(PDO)。
  • 核心注意:事务清理、连接有效性验证、连接数控制。
  • 替代方案:如果连接池管理复杂度高,考虑使用数据库中间件(如 ProxySQL、PgBouncer)。

如果需要进一步了解特定场景(如 Laravel/Symfony 中的持久连接配置),可以告诉我具体框架,我可以给出更详细的代码示例。

抱歉,评论功能暂时关闭!