wait_timeout不是最大生命周期参数,仅控制空闲连接超时;真正决定连接最长存活时间的是连接池的maxLifetime(如HikariCP)或pool_recycle(SQLAlchemy),必须严格小于wait_timeout并预留30秒缓冲,否则仍会复用已被服务端关闭的连接。

MySQL 服务端不控制连接的“最长生命周期”,只管空闲超时;真正能设最大存活时间的,是客户端连接池(比如 HikariCP、SQLAlchemy)或驱动层。
为什么 wait_timeout 不等于最大生命周期
这个参数只对 Sleep 状态的连接生效——也就是完全没发任何语句、纯挂起的连接。只要应用用连接池发心跳(如每 30 秒执行一次 SELECT 1),它就永远不会进入 Sleep,wait_timeout 就形同虚设。
更关键的是:wait_timeout 触发的是服务端单方面 KILL,客户端毫不知情;下次复用时直接报 Lost connection to MySQL server during query 或 MySQL server has gone away。
它也不清理会话资源:CREATE TEMPORARY TABLE、@user_var、预处理语句、线程私有内存(如 sort_buffer_size)全留着,导致内存缓慢上涨。
HikariCP 的 maxLifetime 必须小于 wait_timeout
这是唯一可控、可落地的方案:让连接池自己定期“退休重来”。maxLifetime 是连接从创建起最多活多久(单位毫秒),到期强制关闭并新建。
- 查当前
wait_timeout值:SELECT @@global.wait_timeout;(别看@@wait_timeout,那是会话级,可能被初始化 SQL 覆盖) - 若返回
28800(8 小时),建议设maxLifetime=28000000(7 小时 46 分钟) - 若云数据库(如阿里云 RDS)禁止改
wait_timeout,那就只能靠客户端适配——比如设成25920000(7 小时) - Spring Boot 配置项是
spring.datasource.hikari.max-lifetime,别漏掉单位是毫秒
SQLAlchemy / pymysql 怎么设等效机制
SQLAlchemy 没有 maxLifetime,但 pool_recycle 效果一致(单位秒):
create_engine(
"mysql+pymysql://u:p@h/d",
pool_recycle=25920, # 7.2 小时,必须小于 wait_timeout
pool_pre_ping=True # 每次取连接前先跑 SELECT 1,避开已断连
)pymysql 自身无连接池,也不自动检测有效性;如果不用 SQLAlchemy 或 DBUtils,就得自己写定时重连逻辑——不推荐。
注意:pool_pre_ping=True 不能替代 pool_recycle,它只防中间断连,不解决线程内存累积问题。
容易被忽略的三个硬约束
很多团队调完 maxLifetime 还出问题,往往卡在这三点:
-
maxLifetime必须严格小于wait_timeout,且至少留 30 秒缓冲——否则连接池会把已被 MySQL 关闭的连接再分发出去 - 只设
maxLifetime不够,还得配idleTimeout(空闲连接回收)和connection-timeout(获取连接超时),三者叠加才覆盖所有老化路径 - MySQL 5.7.3+ / Connector/J 8.0.14+ 才支持
mysql_reset_connection(),它能在归还连接前清掉会话资源,但依然不能跳过maxLifetime轮换


















