发布于2026-07-15 阅读(0)
扫一扫,手机访问
连接失败重试这事儿,框架可不会替你操心,得靠你自己手动兜底。ThinkPHP 或原生 PDO/mysqli 都不会在连接失败后自动重试。所谓“重试”是业务层逻辑,不是底层驱动能力。你写的 Db::connect() 或 new PDO() 失败了,就真的失败了——除非你自己包一层 try/catch 并重试。

常见错误现象:代码里明明写了 'break_reconnect' => true 却没生效;或者在 Swoole 长连接时,连接断开后请求直接返回 500;又或者事务中报 MySQL server has gone away 后,没重连就抛异常了。
SQLSTATE[HY000] [2006])有意义,语法错误或主键冲突重试毫无作用。usleep(100000)(100ms),避免雪崩式重连。$pdo 或 $conn。break_reconnect 基本失效,得监听 onClose 事件手动重建。PDO::ATTR_ERRMODE 必须设为 PDO::ERRMODE_EXCEPTION,否则连接失败只返回 false,catch 根本捕不到异常,重试逻辑形同虚设。
典型配置漏项:charset=utf8mb4 写在 DSN 里,但 PDO::ATTR_ERRMODE 没设;或者用了 mysql:host=localhost,结果 PHP 尝试走 Unix socket 却路径不对,报错 No such file or directory,你以为是密码错,其实只是 host 解析问题。
mysql:host=127.0.0.1;port=3306;,绕过 localhost → socket 的歧义。mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=test。caching_sha2_password 会导致 PDO 连接失败且不报具体原因。事务中连接断开,框架会自动 rollback,但事务状态已破坏。此时若直接重试原 Db::transaction() 回调,会报 There is no active transaction。
正确做法是把整个事务逻辑包进循环,每次失败都新建事务上下文。
for ($i = 0; $i < 3; $i++) { try { Db::transaction(function () { Db::table('order')->where('id', 123)->update(['status' => 'paid']); }); break; } catch (think\db\exception\PDOException $e) { if ($i === 2) throw $e; usleep(100000); }}break_reconnect 在事务中不触发重连,它只作用于单条查询执行前的连接探活。$pdo->beginTransaction() 和 $pdo->commit() 也得在每次循环里重新调用。PHP-FPM 场景下每次请求都是新进程,连接失败重试尚可接受;但在 Swoole Worker 或 Lara vel Octane 中,一个连接复用几十秒甚至几分钟,break_reconnect 不起作用,靠重试反而掩盖真实问题。
真正该做的是:连接空闲超 30 秒后主动 ping,失败则 close 并重建;或在每次查询前加 $pdo->getAttribute(PDO::ATTR_CONNECTION_STATUS) 判断是否活跃(部分驱动支持)。
mysqli_ping(),它在某些 MySQL 版本和 mysqli 驱动组合下返回 true 却实际不可用。SELECT 1,并设置 PDO::ATTR_TIMEOUT 防卡死。wait_timeout 和 interactive_timeout MySQL 参数必须与应用层探活间隔匹配,否则刚 ping 完就断。重试本身不是银弹,它掩盖的是连接生命周期管理缺失。真正关键的不是“多试几次”,而是明确知道什么时候该重建连接、什么时候该告警、什么时候该降级——这些边界往往藏在 MySQL 的 wait_timeout、Swoole 的 worker_num、以及你自己的业务 SLA 里。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8