pdo::attr_timeout只控制查询执行超时,不控制连接建立;建连超时需用pdo::mysql_attr_connect_timeout,且必须在params中显式配置,单位为秒。

PDO::ATTR_TIMEOUT 只控制查询执行,不控制连接建立
很多人配了 PDO::ATTR_TIMEOUT 却发现“连不上数据库时还是卡 30 秒”,因为这个参数根本不管建连——它只在连接已成功后,对 query()、execute() 这类操作生效。一旦 SQL 被发到 MySQL 但迟迟没返回(比如锁表、大排序、慢查询),它才起作用。
- 单位是秒,设为
5表示某条语句执行超过 5 秒就抛PDOException - TP6.0 及更早版本可能完全忽略该配置,需确认是否用的是 think-orm v3.0+(TP6.1+)
- 若你遇到首次请求就卡住,别调这个,去查 DSN、网络或 MySQL 服务端的
connect_timeout
PDO 建连超时得靠 MYSQL_ATTR_CONNECT_TIMEOUT
PDO::ATTR_TIMEOUT 不管建连,真正能干预“连接数据库”这一步的,是 PDO::MYSQL_ATTR_CONNECT_TIMEOUT。它由 mysqlnd 驱动解析,直接作用于 TCP 握手阶段。
- 必须显式传入 PDO 构造函数第四个参数(options 数组),写成
[PDO::MYSQL_ATTR_CONNECT_TIMEOUT => 3] - 单位是秒,不是毫秒;设为
0表示无限等待(危险!) - 仅对 MySQL 有效,PostgreSQL/SQLite 不识别该常量
- ThinkPHP 中不能靠 config/database.php 的
timeout字段触发它,必须进params里手动加
为什么有时设了还卡住?常见失效场景
即使写了 PDO::MYSQL_ATTR_CONNECT_TIMEOUT,仍可能卡在系统级 TCP 超时(Linux 默认 75 秒),原因通常是:
- MySQL 服务端未运行,或防火墙拦截了 3306 端口 —— PDO 无法区分“连不上”和“连得慢”,只能等底层 socket 超时
- DSN 中
host=localhost导致走 Unix socket(绕过 TCP 层),此时MYSQL_ATTR_CONNECT_TIMEOUT完全无效 - CLI 模式下,PHP 的
default_socket_timeoutini 设置会覆盖 PDO 层设置(需同步调整) - 用了
Db::connect(['dsn' => '...'])临时连接方式,options 参数根本没传给 PDO 构造函数
ThinkPHP 里怎么正确写配置
在 config/database.php 的具体数据库配置节点(如 'mysql')中,必须把超时参数塞进 params 数组,且要同时兼顾建连和查询两个阶段:
- 建连超时:
'params' => [PDO::MYSQL_ATTR_CONNECT_TIMEOUT => 3] - 查询超时:
'params' => [PDO::ATTR_TIMEOUT => 5] - 错误模式必须设为异常:
'params' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION],否则超时异常会被静默吞掉 - 多连接场景下(如
'log'、'slave'),每个连接配置都要单独声明params,不能指望继承
最容易被忽略的是:break_reconnect => true 只对“查询中连接断开”有效,对建连失败毫无作用。建连超时必须靠 MYSQL_ATTR_CONNECT_TIMEOUT + 正确的 host 和网络环境兜底。











