MySQL客户端连接断开主因是TCP空闲超时被中间设备强制回收,解决需调整Linux内核tcp_keepalive_time(建议600)、intvl(30)、probes(3)参数,并在客户端显式启用SO_KEEPALIVE,云数据库则依赖客户端健康检查与重试机制。

MySQL客户端连接断开是因为TCP空闲超时
MySQL本身不主动管理TCP连接的保活,底层依赖操作系统级的tcp_keepalive机制。当网络中间设备(如NAT网关、防火墙、负载均衡)在连接空闲一段时间后强制回收连接,客户端再发请求就会遇到Lost connection to MySQL server during query或MySQL server has gone away。这不是MySQL配置错了,而是TCP链路被掐断了。
解决方向有两个:一是在客户端侧缩短探测间隔、加快发现断连;二是在服务端侧避免被中间设备误杀——但注意,MySQL服务端wait_timeout和interactive_timeout只控制MySQL内部会话生命周期,不影响TCP连接本身是否存活。
Linux系统级tcp\_keepalive参数怎么调
MySQL用的是系统默认的TCP keepalive行为,必须改/proc/sys/net/ipv4/tcp_keepalive_*三个内核参数。不改的话,Linux默认是2小时才发第一个探测包(太晚),基本赶不上云环境常见的5–30分钟连接清理策略。
-
tcp_keepalive_time:连接空闲多久后开始发keepalive探测(单位秒),建议设为600(10分钟)或更低 -
tcp_keepalive_intvl:两次探测之间的间隔(单位秒),建议30 -
tcp_keepalive_probes:连续失败多少次后判定连接断开,建议3(即90秒内收不到响应就断)
临时生效(重启失效):
echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time echo 30 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
永久生效:写入/etc/sysctl.conf,加一行net.ipv4.tcp_keepalive_time = 600等,然后运行sysctl -p。
MySQL客户端要不要自己设置SO\_KEEPALIVE
可以,而且推荐。MySQL客户端(比如mysql命令行、Python的pymysql、Go的mysql驱动)默认不一定开启socket级别的SO_KEEPALIVE选项,即使系统参数调好了,如果应用层没启用,照样不会发探测包。
- MySQL命令行客户端:从8.0.27起默认开启,老版本可加
--enable-cleartext-plugin无效,得靠系统参数兜底 - Python
pymysql:初始化连接时传autocommit=True, read_timeout=30, write_timeout=30, connect_timeout=10没用,必须显式开keepalive=True(v1.1.0+支持) - Java JDBC:URL里加
?tcpKeepAlive=true&tcpKeepAliveTime=600000&tcpKeepAliveInterval=30000(注意单位是毫秒)
验证是否生效:用ss -tno | grep :3306看连接状态里有没有keepalive字样;或者抓包看是否有TCP ACK探测帧。
云数据库(RDS/Aurora)能调这些吗
不能直接调。阿里云RDS、AWS RDS、腾讯云CDB这些都屏蔽了宿主机内核参数修改权限,你连/proc/sys/net/ipv4/都进不去。这时候只能靠客户端侧发力:
- 应用层加连接池健康检查(比如HikariCP的
connection-test-query或validation-timeout) - 缩短
wait_timeout(比如设成300),让MySQL主动断连,比静默断连更容易捕获和重连 - 客户端代码里对
OperationalError(如错误码2013、2006)做透明重试,但注意幂等性
有些云厂商提供“连接保持”开关(比如阿里云RDS的“长连接优化”),本质是代理层帮你做了心跳透传,但不是所有产品都支持,得查对应文档确认。
真正容易被忽略的是:改完系统参数后,已建立的连接不会自动继承新值,必须重建连接才生效。也就是说,调整完tcp_keepalive_time,得重启应用或清空连接池,否则旧连接还是按老规则走。











