MySQL连接被服务端主动断开主因是wait_timeout和interactive_timeout参数默认8小时超时,需同步调整并配合应用层连接池校验、TCP Keepalive优化及中间设备超时对齐。
MySQL 连接被服务端主动断开:查 wait_timeout 和 interactive_timeout
mysql 默认会在空闲 8 小时后关闭连接,这不是网络问题,而是服务端主动清理。你看到的 lost connection to mysql server during query 或 mysql server has gone away,大概率是这两个参数在起作用。
检查当前值:
SHOW VARIABLES LIKE '%timeout%';
常见误区是只改 wait_timeout,但客户端连接类型(比如 Python 的 pymysql 默认走非交互式路径)实际受 interactive_timeout 控制。两者需同步调高,例如设为 28800(8 小时)或更保守的 14400(4 小时):
SET GLOBAL wait_timeout = 14400;<br>SET GLOBAL interactive_timeout = 14400;
- 修改仅对新连接生效,已有连接仍按旧值计时
- 需有
SUPER权限;生产环境建议写入配置文件my.cnf的[mysqld]段落,避免重启失效 - 值过大可能积累大量空闲连接,压垮连接数上限(
max_connections)
应用层连接池没复用或没校验:Python/Java 客户端常见失联
即使 MySQL 超时调长了,如果应用每次请求都新建连接、不复用,或复用前不检测连接是否还活着,照样会掉线。典型表现是首次请求正常,隔几分钟后再请求就报错。
以 Python 的 SQLAlchemy 为例,关键不是加 pool_recycle,而是要让它真正生效:
-
pool_recycle=3600表示连接最多存活 1 小时,到期自动重建 —— 但必须配合pool_pre_ping=True,否则它只在归还时检查,不保证取出时可用 - 不要依赖
echo=True或日志判断连接状态,得靠pre_ping主动发SELECT 1 - 若用
pymysql直连,务必在connect()后手动执行cursor.execute("SELECT 1")做探活,不能假设 connect 成功就等于链路畅通
TCP 层心跳没打开:Linux 内核默认不发 Keepalive 包
MySQL 协议本身不带心跳,底层靠 TCP Keepalive 维持链路。但 Linux 默认设置极保守:net.ipv4.tcp_keepalive_time = 7200(2 小时才发第一个探测包),远超 MySQL 的 wait_timeout,导致中间 NAT/防火墙早把连接清掉了。
必须显式开启并缩短间隔(尤其在云环境、有负载均衡器时):
- 应用进程启动前,用
setsockopt开启:socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - 进一步控制节奏(以 Python 为例):
socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60)(空闲 60 秒开始探测)socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)(每 30 秒发一次)socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)(连续 3 次无响应才断) - 注意:这些选项需在连接建立前设置,connect() 之后再设无效
云服务商或中间设备静默丢包:别全信“网络稳定”
阿里云 SLB、AWS ELB、甚至某些企业级防火墙,会在连接空闲一定时间(常见 300–900 秒)后直接删掉 NAT 映射表项,且不发 FIN/RST。此时客户端和 MySQL 都不知道连接已断,直到下次发包才触发超时 —— 这就是最让人抓狂的“看起来好好的,一用就崩”。
应对策略不是调大所有 timeout,而是让心跳节奏严苛于中间设备的清理阈值:
- 查清你所用云厂商文档里明确写的“空闲连接超时时间”,比如 AWS NLB 是 350 秒,那就把 TCP Keepalive 的
TCP_KEEPIDLE设为 ≤ 300 - MySQL 的
wait_timeout必须小于这个值,否则服务端先断,反而掩盖了中间设备的问题 - 如果无法确认中间设备策略,宁可设激进些(如
wait_timeout=300+pool_recycle=240+TCP_KEEPIDLE=180),用可控的重连代替不可控的静默失败
真正的难点不在参数怎么填,而在于你得同时盯住四层(TCP)、七层(MySQL)、中间网元、应用框架这四个层面的超时逻辑,少一个对齐,就会在某个时间点突然掉链子。











