thinkphp 默认不自动重连mysql断开连接,需手动干预:通过ping检测+reconnect或自定义connection重试非事务查询,并配合tcp keepalive与mysql timeout协同配置。

MySQL 连接超时后 thinkphp 不自动重连?默认不支持
ThinkPHP(6.x / 7.x)底层用的是 PDO,而 PDO 本身没有内置「连接失效后自动重试」逻辑。一旦 MySQL 因 wait_timeout 或网络抖动断开,后续查询大概率抛出 PDOException: MySQL server has gone away,框架不会帮你重连,而是直接报错。
常见现象:定时任务跑着跑着崩了;低频接口隔十几分钟首次访问失败;云数据库(如阿里云 RDS)启用了较短的 wait_timeout(默认 8 小时,但有些托管环境设成 300 秒)时特别明显。
实操建议:
- 别依赖框架自动恢复——
thinkphp的Connection类在执行query前只检查连接是否“已初始化”,不验证是否“仍活跃” - 必须主动干预:要么在执行前 ping,要么捕获异常后手动 reconnect
- 注意
mysql.ping配置项在 ThinkPHP 中仅控制是否在初始化时发一次PING,不是运行时保活手段
Db::execute() 前加 $connection->ping() 最简兜底方案
适用于大多数非高并发、对延迟不敏感的业务场景。不是“优雅重连”,但能快速止血。
关键点:
-
ping()是 PDO 扩展提供的方法,ThinkPHP 的Connection对象可通过getPdo()拿到原生 PDO 实例,再调用ping() - 它不发 SQL,只做轻量握手;失败则说明连接已断,可安全调用
reconnect() - 别在每个
Db::table()->where()->find()前都加——性能损耗明显;建议只包裹在可能长时间闲置后的首次操作(如命令行任务开头、HTTP 请求入口)
示例(命令行任务中):
$connection = Db::getConnection();
if (!$connection->getPdo()->ping()) {
$connection->reconnect();
}
Db::table('user')->where('id', 1)->find(); // 安全执行
自定义 Connection 子类 + 重写 query() 实现透明重试
适合需要全局生效、又不想到处加 ping 的项目。核心是拦截查询失败,判断错误码是否为连接中断,然后重连并重试一次。
注意事项:
- 只重试**非事务内**的查询——事务中重连会导致状态不一致,直接放弃
- 必须过滤错误码:
HY000+2006(MySQL server has gone away)或2013(Lost connection)才触发重连 - 重试仅限一次,避免死循环;重连失败就让异常透出
- ThinkPHP 6.1+ 的
Connection支持构造时传入pdo实例,便于测试替换
示例片段(重写 query):
public function query($sql, $bind = [], $master = false, $pdo = null)
{
try {
return parent::query($sql, $bind, $master, $pdo);
} catch (\PDOException $e) {
if ($this->inTransaction()) {
throw $e;
}
if (in_array($e->getCode(), ['2006', '2013']) && $e->getMessage() === 'SQLSTATE[HY000]: General error: 2006 MySQL server has gone away') {
$this->reconnect();
return parent::query($sql, $bind, $master, $pdo);
}
throw $e;
}
}
云环境必须配 keepalive 和合理 wait_timeout 对齐
光靠 PHP 层重连治标不治本。很多超时其实源于中间网络设备(SLB、NAT 网关)主动 kill 空闲连接,这时即使 PDO 认为自己连着,发包也会被丢弃。
必须协同配置:
- MySQL 侧:确认
wait_timeout和interactive_timeout值(推荐设为 600~1800),并通过SHOW VARIABLES LIKE '%timeout%'核对 - PHP PDO DSN 加
options=[PDO::ATTR_TIMEOUT=>5]无用,那是连接建立超时;真正要加的是PDO::MYSQL_ATTR_KEEPALIVE => true - Linux 内核级 keepalive:确保
/proc/sys/net/ipv4/tcp_keepalive_time≤ MySQLwait_timeout,否则连接在 TCP 层就被断了,PDO 来不及感知
DSN 示例(含 keepalive):
mysql:host=127.0.0.1;port=3306;dbname=test;charset=utf8mb4;unix_socket=/tmp/mysql.sock
连接后立即启用:
$pdo = new \PDO($dsn, $user, $pass, [
\PDO::MYSQL_ATTR_KEEPALIVE => true,
]);
最麻烦的其实是混合场景:长连接池(如 Swoole)+ 云数据库 + 自动扩缩容。这时候连接有效性判断不能只靠 ping,得结合连接创建时间戳、上次使用时间、以及是否跨 worker 共享等上下文——这些细节,框架不会替你决定。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











