pdo::attr_timeout仅控制sql执行超时,不作用于连接建立;连接超时需通过dsn中connect_timeout或php.ini的default_socket_timeout配置;重试需手动实现,break_reconnect仅处理查询异常后的重连。

PDO::ATTR_TIMEOUT 在 ThinkPHP 里根本不管连接建立超时
PDO::ATTR_TIMEOUT 看起来像万能开关,但实际只控制「SQL 执行阶段」的等待上限,比如 Db::table('user')->select() 卡在等 MySQL 返回结果时最多忍 5 秒。它对 TCP 连接失败、DNS 解析卡住、MySQL 拒绝认证这些「连都连不上」的场景完全无效——此时 PHP 会卡在 new PDO() 内部,直到系统 socket 超时(常为 60~120 秒),PDO::ATTR_TIMEOUT 压根没机会触发。
网络抖动时真正起作用的是 DSN 中的 connect_timeout
要让连接阶段快速失败,必须把超时塞进 DSN 字符串里:
- PHP ≥ 8.1 + pdo_mysql 扩展:支持
mysql:host=127.0.0.1;port=3306;dbname=test;connect_timeout=3 - 低版本 PHP 或不支持该参数时,只能靠
default_socket_timeout全局配置(php.ini 中设,单位秒) - ThinkPHP 的
config/database.php中,需手动拼进dsn字段,不能只写在params里
示例配置片段:
$config = [
'type' => 'pdo_mysql',
'dsn' => 'mysql:host=rds.example.com;port=3306;dbname=app;connect_timeout=3',
'params' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION],
];
重试逻辑必须自己写,框架不会自动帮你多试几次
break_reconnect => true 不是“网络抖动自动重连”,它只在查询执行中抛出 PDO 异常(如 SQLSTATE[HY000] [2006] MySQL server has gone away)后尝试重建连接并重跑一次 SQL。但它不处理连接建立失败、DNS 失败、SSL 握手超时等场景,也不支持指数退避或自定义重试次数。
- 关键操作(如支付回调)应自己用
try/catch包裹Db::transaction(),捕获think\db\exception\PDOException - 重试前加
usleep(100000)(100ms),避免雪崩 - 最多重试 2~3 次,再失败必须告警,而不是继续硬扛
- 事务中重试需手动 rollback 后重新 begin,否则状态错乱
最易被忽略的点:CLI 和 Swoole 场景下连接生命周期完全由你负责
Web 请求天然短生命周期,连接断了下次请求重建即可;但 CLI 任务或 Swoole Worker 可能持续运行数小时,wait_timeout(默认 8 小时)早被耗尽。此时:
-
break_reconnect仅对首次查询生效,后续查询仍可能因连接空闲过久而失败 - 必须在每次查询前做连接有效性探测,推荐
$pdo->exec('SELECT 1')(比getAttribute()更准) - 不要在事务中探测,避免隐式 commit 或破坏隔离级别
- Swoole 需监听
onClose事件主动 close 连接,否则连接泄漏
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











