PDO连接超时不能靠timeout配置项生效,必须显式传入PDO::ATTR_TIMEOUT(秒级,通用)或PDO::MYSQL_ATTR_CONNECT_TIMEOUT(秒级,mysqlnd驱动+PHP≥7.0),两者行为、单位、生效条件完全不同。

PDO 连接超时不能靠 timeout 配置项生效,必须显式传入 PDO::ATTR_TIMEOUT 或 PDO::MYSQL_ATTR_CONNECT_TIMEOUT,且两者行为、单位、生效条件完全不同。
为什么 timeout => 3 在 PDO 配置里完全无效
这是最常被误写的配置。PDO 不识别字符串键 timeout,也不读取 DSN 中的 timeout=3(除非 PHP ≥ 8.1 且用 mysqlnd 驱动)。写成这样:
$pdo = new PDO($dsn, $user, $pass, ['timeout' => 3]);
等于没设——PDO 会退回到系统级 TCP 超时(Linux 默认 75 秒),脚本卡住不报错。
-
PDO::ATTR_TIMEOUT控制的是「PDO 内部等待连接建立完成」的秒级超时,仅对mysql和pgsql有效,单位是秒 -
PDO::MYSQL_ATTR_CONNECT_TIMEOUT是 MySQL 专用属性,控制底层mysql_real_connect()的 connect 阶段,单位也是秒,但仅在 mysqlnd 驱动 + PHP ≥ 7.0 下才真正生效 - 两个参数可以同时设,但
PDO::ATTR_TIMEOUT优先级更高;若只设一个,推荐用PDO::ATTR_TIMEOUT,兼容性更好
PDO::ATTR_TIMEOUT 怎么写才真正起作用
它只在 new PDO() 构造时通过第四个参数数组传入,且必须是整数(不能是字符串):
立即学习“PHP免费学习笔记(深入)”;
$pdo = new PDO($dsn, $user, $pass, [
\PDO::ATTR_TIMEOUT => 3,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
]);
- 值为
0表示无限等待,生产环境严禁使用 - 设太小(如
0.5)会导致偶发性连接失败,尤其在 DNS 解析慢或网络抖动时 - 若用 ThinkPHP,必须写在
config/database.php对应数据库配置的'params'数组里,例如:'params' => [\PDO::ATTR_TIMEOUT => 3] - 临时连接如
Db::connect(['dsn' => '...'])无法传入该参数,PDO 默认用0,等同于不设
为什么用了 PDO::ATTR_TIMEOUT 还是卡 75 秒
常见于 Windows phpEnv 或旧版 PHP(
- 确认
phpinfo()中是否启用mysqlnd,而非废弃的libmysql;后者下PDO::MYSQL_ATTR_CONNECT_TIMEOUT完全无效 - host 写
localhost会走 Unix socket,绕过 TCP 层,PDO::ATTR_TIMEOUT失效;必须改用127.0.0.1 - CLI 模式下,
php.ini中的default_socket_timeout = 30仍会兜底影响,建议同步调低至10避免冲突 - 验证是否生效:故意把 MySQL host 改成一个不存在的 IP(如
192.168.99.99),看是否真在 3 秒后抛出PDOException,而不是卡满 75 秒
连接建好了,但查询中途断开怎么办
PDO::ATTR_TIMEOUT 只管「连上」,不管「连上后空闲多久被 MySQL 杀掉」。如果报 MySQL server has gone away,那是 MySQL 的 wait_timeout 在作祟:
- ThinkPHP 必须配
'break_reconnect' => true,否则复用已断连接会直接崩 - 这个选项只对「查询执行中连接断开」有效,对「连接根本建不上」毫无作用
- CLI/队列任务需根据单次最长执行时间,反推设置 MySQL 的
wait_timeout(如设为 300),并确保与 PHP 端PDO::ATTR_TIMEOUT错开(前者 > 后者) - 不要依赖
try/catch包裹Db::table()->select()——连接失败发生在查询前,异常抛在new PDO()那一刻
真正要命的不是设不设超时,而是设在哪一层、谁在管、谁又在兜底。PDO 层、MySQL 服务层、PHP socket 层、ThinkPHP 连接池层——四层超时参数各管一段,漏掉任何一层,都可能让脚本在某个环节无声卡死。



















