MySQL连接空闲8小时后自动断开导致“2006 MySQL server has gone away”错误,解决方法包括:启用ThinkPHP的break_reconnect机制、CLI环境动态配置重连、Swoole下手动心跳探活、调低MySQL的wait_timeout参数、事务中手动封装安全重试逻辑。

如果您在ThinkPHP应用中遇到MySQL连接因空闲超时被服务器主动断开,导致后续查询抛出SQLSTATE[HY000] [2006] MySQL server has gone away错误,则需通过配置断线重连机制来恢复连接。以下是稳定生效的多种配置方法:
一、启用框架内置break_reconnect机制
该方法利用ThinkPHP原生支持的断线自动重建能力,在PDO异常触发后关闭旧连接并新建连接,仅对PDO::ERRMODE_EXCEPTION模式下的网络类错误(如2006)生效,不适用于语法错误或事务内中断。
1、打开数据库配置文件config/database.php,定位到对应数据库连接配置(如mysql)。
2、确保'type'值为'pdo_mysql',不可使用mysqli或mysql驱动。
立即学习“PHP免费学习笔记(深入)”;
3、将'break_reconnect'设置为true。
4、在'params'数组中显式添加PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION。
5、可选:添加'break_match_str' => ['2006', 'Error while sending']以增强错误码匹配精度。
6、确认'debug' => true或已开启日志记录,否则重试过程无可见输出。
7、保存配置后重启Web服务或CLI进程使配置生效。
二、CLI环境独立启用重连(规避FPM风险)
在Web请求(FPM)与队列/命令行(CLI)共用同一套数据库配置时,直接全局开启break_reconnect可能导致FPM下事务语义被破坏;本方案通过运行时动态覆盖配置,实现CLI专属启用,保障FPM环境安全。
1、在队列启动脚本或自定义命令入口文件顶部引入数据库配置。
2、执行$dbConfig = config('database.connections.mysql');获取原始配置。
3、手动设置$dbConfig['break_reconnect'] = true;。
4、调用\think\Db::init($dbConfig);重新初始化数据库连接实例。
5、后续所有Db操作将基于该启用重连的新配置执行。
6、此方式避免修改全局配置,特别适用于ThinkPHP-Queue长驻进程场景。
三、配合Swoole常驻进程的手动探活重连
在Swoole Worker或Task进程等长生命周期环境中,框架级break_reconnect可能失效,因其作用域限于单次请求;需在连接空闲前主动探测并重建,防止被MySQL服务器单方面断开。
1、监听Swoole Server的onWorkerStart事件,在Worker启动时初始化数据库连接。
2、使用Timer::tick()设置周期性心跳检测,例如每3000毫秒执行一次。
3、在定时器回调中调用Db::query('SELECT 1')验证连接有效性。
4、若抛出\PDOException且含2006或Broken pipe字样,则执行Db::close()强制关闭当前连接。
5、下次业务查询将自动触发新连接建立,无需等待失败后重试。
6、该方案绕过框架限制,适用于Swoole+ThinkPHP混合部署。
四、调整MySQL服务端参数协同优化
仅靠客户端重连无法根治空闲断连问题,需同步降低MySQL服务器端空闲超时阈值,使其与应用心跳节奏匹配,减少“连接已断但客户端未知”的窗口期。
1、登录MySQL服务器,执行SHOW VARIABLES LIKE 'wait_timeout';查看当前值。
2、若值大于300(5分钟),建议调低至300或600以适配PHP CLI任务间隔。
3、执行SET GLOBAL wait_timeout = 300;临时生效(需SUPER权限)。
4、永久生效需编辑MySQL配置文件my.cnf,在[mysqld]段落添加wait_timeout = 300。
5、重启MySQL服务或执行FLUSH PRIVILEGES;使配置加载。
6、同步检查interactive_timeout值,Web请求不受其影响,但CLI进程默认按wait_timeout判定。
五、事务场景下的安全重试封装
框架默认不在事务中触发重连,因重连后事务上下文已丢失,强行复用会导致“There is no active transaction”错误;必须由业务层显式控制重试逻辑,确保每次重试都开启全新事务。
1、将关键事务操作包裹在for循环中,限定最大重试次数为3次。
2、每次循环开始前,使用Db::transaction(function() { ... });开启新事务。
3、捕获\think\db\exception\PDOException异常,判断错误信息是否包含2006。
4、若为第3次重试仍失败,则不再捕获,直接抛出原始异常。
5、每次重试前插入usleep(100000)(100ms延迟),防止瞬时重试雪崩。
6、该封装必须由开发者自行编写,框架不提供通用事务重试组件。



















