connection reset by peer是tcp层rst包导致的强制断连,非mysql协议层问题;需通过tcpdump定位rst源ip,排查中间设备空闲超时、服务端accept队列溢出或客户端未启用so_keepalive。

Connection reset by peer 是 TCP 层 RST,不是 MySQL 主动关的
看到这个报错,第一反应别去翻 wait_timeout 或 max_allowed_packet——那些只会触发 ERROR 2013 或 ERROR 2006,属于协议层断连。而 Connection reset by peer 是操作系统或中间设备发的 TCP RST 包,MySQL 进程本身几乎从不发 RST(它只发 FIN)。这意味着:问题不在 SQL 或会话配置,而在网络链路本身。
常见诱因包括:
- 云厂商 NAT 网关/SLB 的空闲超时(通常 300–900 秒),比 MySQL 的
wait_timeout更早动手 - 本地防火墙、企业网关、WAF 在连接空闲后直接 RST,不通知后端
- Linux 内核
tcp_keepalive_time默认 7200 秒,根本赶不上中间设备 5 分钟就清连接 - 客户端驱动没启用
SO_KEEPALIVE,系统参数调对了也白搭
先用 tcpdump 确认 RST 谁发的
在服务端执行:
/usr/sbin/tcpdump -i eth0 -n -nn 'tcp[tcpflags] & (tcp-rst) != 0 and host <client-ip>' -w reset.pcap</client-ip>
抓包后看 RST 包源 IP:
- 如果源 IP 是客户端真实出口 IP → 客户端侧主动 RST(比如进程崩溃、杀进程、强制关 socket)
- 如果源 IP 是负载均衡器或 NAT 网关 IP → 中间设备掐断,立刻查其空闲超时设置
- 如果源 IP 是服务端本机 IP,且时间戳紧贴三次握手之后 → 服务端 accept 队列溢出或进程已挂但端口仍监听
注意:telnet 或 nc -zv 只能验证端口通不通,无法暴露空闲超时问题;必须空闲几分钟后再发查询,才能复现。
Linux 服务器必须调 keepalive 内核参数
MySQL 进程跑在 Linux 上,完全依赖内核的 TCP keepalive 探测机制。默认值根本不够用:
-
echo 300 > /proc/sys/net/ipv4/tcp_keepalive_time(首次探测前空闲秒数) -
echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl(重试间隔) -
echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes(最多探几次)
永久生效需写入 /etc/sysctl.conf 并运行 sysctl -p。验证是否生效:
ss -i state established '( dport = :3306 )' | grep keep
没输出 keepalive 字段,说明没生效。
客户端驱动必须显式开 SO_KEEPALIVE
很多驱动默认关闭 SO_KEEPALIVE,哪怕系统参数调对了,应用层也不会发探测包:
- Python
pymysql:初始化Connection时传keepalive=True(v1.1.0+ 才支持) - Go
database/sql+mysql驱动:DSN 里加timeout=10s&readTimeout=30s&writeTimeout=30s不管保活,得靠SetConnMaxLifetime(5 * time.Minute)强制轮换连接 - DBeaver:连接编辑页 → Driver properties → 设置
socketTimeout和勾选useSSL=false(若非必要)
最危险的是:你以为连接池在复用连接,其实连接早就被 NAT 清掉,第一次查询就触发 RST。保活不是可选项,是必选项。











