wait_timeout不是防止断连的开关,而是控制服务端主动断开空闲连接的时机;真正稳住长连接需让mysql的wait_timeout略大于连接池maxlifetime(如600秒vs540秒),并启用select 1有效性验证,否则必报“mysql server has gone away”。

直接说结论:wait_timeout 不能“防止”长连接断开,它恰恰是控制“何时主动断开”的开关。真正要稳住长连接,得让 MySQL 的断开时间略大于连接池的淘汰时间,并配好有效性验证——否则改了也白改,照样报 MySQL server has gone away。
查清当前生效值,别猜默认值
不同环境实际值差异极大:云数据库常设为 300 秒,Docker 官方镜像默认 28800 秒,某些 PHP 环境甚至继承发行版模板设成 60 秒。不查就调,等于蒙眼修电路。
- 连上 MySQL 执行:
SELECT @@wait_timeout, @@interactive_timeout;—— 注意单位是秒 - 如果两个值不等,说明服务端行为已分裂,后续排查会绕弯子
- 别用
SHOW VARIABLES LIKE '%timeout%',它默认查会话变量;要用SHOW GLOBAL VARIABLES LIKE 'wait_timeout'看全局值
永久修改必须写 my.cnf 并重启 mysqld
临时执行 SET GLOBAL wait_timeout = 600 看似快,但 MySQL 一重启就回滚,且新连接是否继承还取决于配置文件读取顺序。生产环境必须落盘。
- Linux/macOS 下找
/etc/my.cnf或/etc/mysql/my.cnf;phpEnv 用户看/usr/local/phpenv/config/mysql/my.cnf - 只在
[mysqld]段下加两行:wait_timeout = 600和interactive_timeout = 600 - 删掉
[client]段里的同名配置——那只是给mysql命令行用的,不影响服务端判定 - 改完必须执行:
sudo systemctl restart mysql(不是reload)
HikariCP/Druid 连接池参数比数据库更关键
把 wait_timeout 设成 600 秒(10 分钟),但 HikariCP 的 maxLifetime 默认是 30 分钟(1800000 毫秒),连接池根本等不到 MySQL 断它,自己先销毁了;反过来,如果池子 idleTimeout 是 10 分钟,而 MySQL 设成 1 小时,那连接大概率在被 MySQL 杀之前就被池子回收了。
- 正确做法:MySQL
wait_timeout必须略大于连接池的生命周期,例如设为 600 秒,则 HikariCP 的maxLifetime设为 540000 毫秒(9 分钟),idleTimeout设为 300000 毫秒(5 分钟) - HikariCP 必须配:
connection-test-query=SELECT 1+test-on-borrow=true(开发)或test-while-idle=true+validation-timeout=3(生产) - Druid 必须开:
testWhileIdle=true且timeBetweenEvictionRunsMillis≤wait_timeout的 1/2(比如 timeout=600,这里设 300000) - 别信
autoReconnect=true——MySQL 5.5+ 已废弃,它不重连,只抛CommunicationsException,还可能破坏事务一致性
MySQL 5.7 和 8.0 对事务中空闲计时逻辑不同
这个差异极隐蔽但致命:MySQL 5.7 中,只要连接处于事务中(哪怕只是 BEGIN 后没提交),wait_timeout 计时器就暂停;MySQL 8.0 则不管事务状态,只要没执行任何语句(包括 COMMIT),倒计时照常进行。
- 结果就是:MySQL 8.0 下“事务卡住”的连接更容易被误杀,凌晨批量任务里尤其常见
- 验证方式:
SELECT @@wait_timeout;看当前会话值,再查SHOW VARIABLES LIKE 'wait_timeout';确认全局值,二者不一致说明客户端连接时未正确协商 - 兼容写法:无论哪个版本,都在
my.cnf中同步设置wait_timeout和interactive_timeout为相同值,避免客户端类型识别偏差
最常被忽略的一点:MySQL KILL Sleep 连接后,并不会自动清理会话级临时表、用户变量或 Prepared Statement。如果连接池复用了这个物理连接(尤其是没启用 validation 时),下次执行 SQL 可能因残留状态直接失败,但错误日志里完全看不出和超时有关。











