目录

PDO(mysql驱动)查询超时设置方法

PHP PDO 超时设置误区与解决方案

一、官方说明

PDO::ATTR_TIMEOUT 参数用于设置超时时间(单位:秒),但不同数据库驱动的行为存在差异:

  • SQLite:等待可写锁的最长时间
  • MySQL:默认作为连接超时时间
  • 其他驱动:可能解释为读取超时或连接超时

注意:该参数为 int 类型,且部分驱动可能不支持


二、常见误区解析

1. 连接初始化后设置超时

$db = new PDO($dsn, $user, $pass);
$db->setAttribute(PDO::ATTR_TIMEOUT, 1); // 无效操作

问题本质
PDO 构造函数已开始建立连接,此时修改超时参数对正在进行的连接无影响
表现:仍使用默认 30 秒超时


2. 试图通过 default_socket_timeout 控制

ini_set('default_socket_timeout', 3);
$db = new PDO($dsn, $user, $pass); // 仍使用默认 30 秒

问题根源
PDO 源码(以 PHP 5.6.29 为例)中硬编码了默认超时:

long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);

表现:socket 超时设置被源码默认值覆盖


3. 误用 ATTR_TIMEOUT 控制查询超时

$db = new PDO($dsn, $user, $pass, [PDO::ATTR_TIMEOUT => 1]);
$db->query('SELECT SLEEP(5)'); // 实际等待 5 秒

核心矛盾
官方注释明确说明 PDO_ATTR_TIMEOUT 仅控制连接超时:

PDO_ATTR_TIMEOUT, /* connection timeout in seconds */

表现:查询超时由驱动单独控制,与该参数无关


三、正确解决方案

1. MySQL 场景推荐配置(php.ini)

mysqlnd.net_read_timeout = 5 ; 查询读取超时(单位:秒)
mysqlnd.connect_timeout = 3 ; 连接超时(单位:秒)

生效时机

  • net_read_timeout:控制 query() 超时
  • connect_timeout:控制连接建立超时

验证代码

$db = new PDO($dsn, $user, $pass);
$db->query('SELECT SLEEP(5)'); // 当 net_read_timeout <5 时会抛出异常

2. PostgreSQL 异步处理方案

// 使用异步连接标志
$db = new PDO("pgsql:host=127.0.0.1;dbname=test", $user, $pass, [
    PDO::PGSQL_ATTR_CONNECT_ASYNC => true
]);

// 手动实现超时检测
$startTime = microtime(true);
while (true) {
    if (microtime(true) - $startTime > 2) { // 2秒超时
        throw new Exception("Query timeout");
    }
    // 检查查询状态...
    usleep(100000); // 100ms轮询间隔
}

适用场景
需要精确控制查询超时的复杂场景,可配合 pg_cancel_query() 实现强制中断


四、参数对比表

配置方式 适用场景 作用范围 驱动依赖 可控性
PDO::ATTR_TIMEOUT 简单连接超时 连接建立阶段
mysqlnd.net_read_timeout 精确查询控制 查询执行阶段 MySQL
pg_cancel_query() 复杂超时处理 全流程 PostgreSQL 极高
default_socket_timeout 全局socket控制 所有socket操作

五、最佳实践建议

  1. MySQL 用户:优先通过 php.ini 设置 mysqlnd.net_read_timeout
  2. 连接池场景:保持 PDO::ATTR_TIMEOUT 设置,配合连接池健康检查
  3. 实时性要求高:使用异步驱动 + 自定义超时检测
  4. 生产环境验证:通过 SLEEP() 函数测试实际超时行为
  5. 驱动差异规避:对多数据库兼容场景,建议封装超时处理适配层

紧急修复:若发现超时配置未生效,应优先检查 php.ini 配置是否被 ini_set() 动态修改覆盖,或通过 phpinfo() 确认实际生效值。