break_reconnect=true 仅在pdo抛异常时触发重连,需同时设置pdo::attr_errmode=>pdo::errmode_exception,且仅对查询中断开生效;事务内失效,持久连接不解决断线问题,cli/swoole需主动探活。

break_reconnect=true 为什么经常不生效
它只在 PDO 抛出异常时触发重连,而 PDO 默认不抛异常——PDO::ATTR_ERRMODE 必须设为 PDO::ERRMODE_EXCEPTION,否则错误被静默吞掉,break_reconnect 彻底失效。常见现象是配置写了 'break_reconnect' => true,但日志里还是直接报 SQLSTATE[HY000] [2006] MySQL server has gone away,没重试痕迹。
另外,这个机制只对「查询执行中连接断开」起作用,不是连接前检测。如果连接已断但 PDO 还没发现(比如 mysqlnd 驱动下空闲超时后仍返回“有效”状态),第一次 ->query() 就会失败并抛异常,重连发生在那次失败之后——意味着业务层必须自己 try/catch,不能指望框架静默恢复。
以下配置缺一不可:
-
'type' => 'pdo_mysql'(mysqli驱动不支持该参数) 'break_reconnect' => true'params' => [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]- 可选但推荐:
'break_match_str' => ['2006', 'MySQL server has gone away'],避免误判其他错误
PDO::ATTR_PERSISTENT 不是断线重连,而是连接复用
PDO::ATTR_PERSISTENT => true 的作用是在 PHP-FPM worker 进程内复用底层 TCP 连接,跳过握手和认证开销。但它完全不处理连接断开问题:一旦 MySQL 主动踢掉空闲连接(比如 wait_timeout=28800 到期),PDO 仍会拿着失效句柄继续用,下次查询就报 MySQL server has gone away,且不会自动重建。
更危险的是,持久连接会残留 SET 语句、临时表、事务状态等上下文。例如一个请求开了事务但没提交,下一个请求复用同一连接,可能直接继承未结束的事务,导致数据错乱或锁等待。
使用前提非常苛刻:
- 必须在
new PDO()构造时传入,运行时调用setAttribute()无效 - DSN、用户名、密码三者必须完全一致,
host=localhost和host=127.0.0.1被视为不同连接池 - MySQL 的
max_connections必须 ≥ PHP-FPM 的pm.max_children,否则必然触发Too many connections - 建议禁用:
PDO::ATTR_EMULATE_PREPARES => false、PDO::MYSQL_ATTR_FOUND_ROWS => false
CLI/Swoole 场景下必须主动探活
Web 请求是短生命周期,每次请求新建连接,break_reconnect 至少能兜住一次失败;但 CLI 脚本、队列任务、Swoole 常驻进程可能运行数小时,连接空闲后大概率被 MySQL 踢掉,而框架不会主动 ping。
最轻量可靠的探测方式是执行 SELECT 1:
try {
Db::getPdo()->exec('SELECT 1');
} catch (\PDOException $e) {
if (strpos($e->getMessage(), 'MySQL server has gone away') !== false) {
Db::close();
Db::connect();
}
}
注意点:
- 不要在事务中执行这个探测,
Db::close()会导致当前事务回滚,破坏一致性 - 建议封装进基类或中间件,在每次查询前统一拦截,而不是散落在各处
- 对于 Swoole,还需监听
onClose事件,在连接关闭时手动重建,否则重连逻辑可能被绕过
事务里断线,重连毫无意义
这是最容易被忽略的致命点:break_reconnect 在事务中完全不工作。一旦 Db::startTrans() 后连接断开,框架直接抛异常,不会尝试重连,也不会自动回滚——你拿到的是一个已中断但未清理的事务状态。
此时即使手动 Db::connect(),新连接也跟原事务无关,继续 Db::commit() 会报错,Db::rollback() 可能静默失败。唯一安全做法是:在事务关键操作前主动探测连接有效性,或把长事务拆成多个短事务,每个事务独立做容错。
真正稳的方案不是靠单个配置开关,而是组合策略:
- Web 接口:开
break_reconnect+PDO::ATTR_ERRMODE_EXCEPTION,配合try/catch捕获首次失败 - 队列/定时任务:关
persistent,每次任务开头SELECT 1探活,失败则Db::close() + Db::connect() - 所有事务逻辑:禁止跨长时间操作,不在事务中做网络 I/O 或 sleep,把连接保活责任收归应用层
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











