简介:针对 Navicat 远程连接 MySQL 时报错 “2013-Lost connection to MySQL server at 'waiting for initial communication packet'” 的问题,这份资料整理了一套从服务器端入手的完整排查与修复方案。资源按“检验安装—修改配置—进入 MySQL—授权用户—Navicat 连接”五个阶段组织,适合在 Linux 云服务器上部署 MySQL 并需要远程管理的开发者、运维人员阅读。说明文档以单个 PDF 文件提供,大小 843KB,内容以 CentOS 7 为例,覆盖定位 my.cnf、注释 bind-address、配置 skip-name-resolve、通过 vim 修改文件、执行 update user set Host='%' 与 flush privileges 等授权操作,并补充了安全组开放 3306、22 端口及使用 Navicat SSH 通道连接的要点。文档中包含命令示例和界面图示,能帮助读者理解问题成因,按步骤操作即可减少常见踩坑,恢复远程数据库连接。这套排错方案已有 20940 人学习,适合作为排查同类初始化阶段断连问题时的参考。
1. 2013-Lost connection at 'waiting for initial communication packet':这到底是谁在等谁
用 Navicat 远程连 MySQL,弹出“2013 - Lost connection to MySQL server at 'waiting for initial communication packet'”,大多数人第一反应是去改 Navicat 的等待超时参数,改完发现毫无用处。因为这个错误信息并不能理解成“Navicat 等太久了”——TCP 三次握手已经完成,问题出在 MySQL 服务端没能在connect_timeout以内把第一个握手包发到客户端,或者发包在半路被截走。Navicat 一直在等一个永远不会到达的包,卡住直到超时,然后给你一条看起来像网络断开的报错。
这个错误常见于跨网段远程连接、云服务器、公司内网跳板机后的数据库,也出现在 MySQL 刚重启完成但 CPU 被打满的瞬间。它跟密码错误无关、跟数据库版本无关、跟驱动基本无关,核心矛盾在网络层和服务端配置。本文按从外到内的顺序拆解:网络有没有通、防火墙有没有拦、MySQL 有没有把包发出来、Navicat 侧怎么配合,最后给你 5 条我亲手踩过的坑。
2. 网络层排查:先用三分钟确认这个错误不是防火墙和路由的锅
2.1 建连三分钟:先看 TCP 通不通,再看 MySQL 听没听
排查 2013 错误时我的顺序永远先从网络层开始。不要一上来就改 MySQL 配置,因为八成问题不在配置。用一条命令判断 TCP 层是否可达:
# 在 Navicat 所在的机器上执行,替换成你的 MySQL 服务器 IP 和端口 nc -vz -w 5 192.168.1.100 3306 # 没有 nc 的环境用 telnet 也一样 telnet 192.168.1.100 3306nc -vz的-w 5表示连接等待 5 秒后失败退出,-v输出详细信息。如果输出Connected to 192.168.1.100 port 3306 succeeded,说明 TCP 层能通;如果卡住直到超时,说明路由或防火墙在丢包;如果直接Connection refused,说明 IP 通但端口没在监听,问题定位到 MySQL 进程本身。
TCP 通了但 Navicat 还是报 2013,就说明三次握手成功了。MySQL 协议里,服务端在 TCP 建连后会立刻发送一个握手数据包,包含协议版本、服务器版本、连接线程 ID、认证插件和 salt。Navicat 等待的就是这个包。TCP 通、握手包不来,通常只有三类原因:服务端connect_timeout太短导致它在发包前就判定客户端超时;服务端负载过高或 DNS 反查卡住导致握手流程没走完;或者包在回程路上被防火墙策略静默丢弃。
2.2 用 tcpdump 看握手包到底去哪了
网络层判断不能只靠猜,抓包是最直接的证据。在 MySQL 服务器上执行:
# 在 MySQL 服务器上抓取来自 Navicat 客户端 IP 的 3306 端口流量 tcpdump -i eth0 -n -s 0 host 192.168.1.50 and port 3306重点关注四种包:客户端发出的SYN、服务端回应的SYN-ACK、连接建立前后的ACK,以及任何RST或重复重传。如果看到服务端回了SYN-ACK但客户端没有继续发包,问题在客户端到服务端的回程;如果SYN进来后什么都没有,说明 MySQL 进程没监听到这个包,问题在服务端接受队列或iptables的 INPUT 链。
有一种非常隐蔽的情况:SYN和SYN-ACK都正常,服务端也发了应用层数据包,但tcpdump里完全看不到任何长度超过 60 字节的包。这通常意味着握手数据包在离开 MySQL 前就被某种机制拦截了,比如bind-address只配置了某个网卡地址,导致数据从错误的接口发出。抓包能把问题范围缩小到“包根本没产生”还是“包发了但没到”,这两者的排查路径截然不同。
2.3 iptables 三条规则:很多 2013 是被防火墙静默拦掉的
DEFAULT DROP策略下只放行了已建立的连接,这是最常见也是最坑的配置。假设 MySQL 服务器的iptables规则长这样:
iptables -A INPUT -p tcp --dport 3306 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT这条规则只允许“已建立连接”的后续包进来,而SYN包属于NEW状态,会被默认策略丢掉。客户端表现是 TCP 连接根本建立不起来,nc会卡住直到超时。但还有一种更隐蔽的写法——NEW状态被放行,RELATED状态没放行,导致握手包中的某个分片被丢。
我习惯直接把 MySQL 端口的规则写成这样:
iptables -A INPUT -p tcp --dport 3306 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPTNEW允许外部发起新连接,ESTABLISHED保证后续通信包畅通。修改后记得测试,不要在远程操作时把自己锁在门外。临时测试可以先iptables -I INPUT 1 -p tcp --dport 3306 -j ACCEPT插到第一条验证,确认无误后再整理规则。
3. MySQL 服务端配置:让初始通信包发得出来、发得及时
3.1 bind-address 与监听地址:远程连不上的头号原因
MySQL 的bind-address参数决定了服务端监听哪个网络接口。默认安装的 MySQL 在很多系统上只监听127.0.0.1,远程连接时 TCP 请求根本到不了 MySQL 进程。nc测试时端口看起来是通的(因为本机回环能连),但跨网段连不上,或者连上了立刻被拒。
查看当前监听情况:
# 在 MySQL 服务器上执行 netstat -tlnp | grep 3306如果输出是127.0.0.1:3306,说明只监听本机回环;如果输出0.0.0.0:3306或::3306,说明监听所有接口。修改bind-address有两种常见做法:改成0.0.0.0监听全部接口,或者指定内网网卡 IP。我的建议是能用具体 IP 就用具体 IP,少暴露一个面就少一份风险。修改配置文件后重启 MySQL:
# 编辑 /etc/mysql/mysql.conf.d/mysqld.cnf 或 /etc/my.cnf,按实际环境路径 [mysqld] bind-address = 0.0.0.0 # 重启 MySQL(不同发行版命令不同) systemctl restart mysqld # 或 service mysql restart注意bind-address不要写成逗号分隔的多个 IP,MySQL 不支持这种写法。如果服务器有多块网卡但只想监听其中一块,直接写那块网卡的 IP。改完用netstat -tlnp再次确认监听地址变化。
3.2 connect_timeout 与 back_log:第一个握手包不发的两个原因
connect_timeout是 MySQL 服务端在 TCP 建连后等待客户端发送第一个数据包的超时时间,默认是 10 秒。很多跨网段连接客户端到服务器的 RTT 本来只有几十毫秒,但服务端负载高导致进程调度延迟,10 秒不够用的情况确实存在。更重要的是 DNS 反查问题——服务端收到 TCP 连接后会尝试反向解析客户端 IP,如果 DNS 服务器不可达,这个阶段会卡很久,connect_timeout就此耗尽。
back_log是 MySQL 在拒绝新连接之前允许排队的连接请求数。当短时间内涌入大量连接,back_log队列满了之后,新的 TCP 连接会被内核直接丢弃,客户端侧表现为等待SYN-ACK超时。这两个参数在 MySQL 配置文件中长这样:
[mysqld] # 默认10秒,跨网段或高峰期可调大到15~20 connect_timeout = 15 # 默认80,连接量大的时候结合内核somaxconn设置 back_log = 300调back_log时要同时看内核参数,因为 MySQL 的back_log最终受net.core.somaxconn限制。建议对齐:
# 查看当前内核backlog上限 sysctl net.core.somaxconn # 超过这个值,MySQL设置无效,需同步调整(临时生效) sysctl -w net.core.somaxconn=1024还有一个容易被忽略的关联参数:wait_timeout和interactive_timeout。前者控制非交互连接空闲多久被服务器关闭(默认 8 小时),后者控制交互式连接。Navicat 这类图形客户端算交互连接,受interactive_timeout控制。如果这个值设置过小,Navicat 挂着不动一会儿连接就被服务端回收,下次操作时表现出的错误通常是“Lost connection to MySQL server during query”,但网络环境恶劣时也会退化成 2013。排查 2013 时顺手看一眼这两个参数没有坏处。
3.3 skip-name-resolve:DNS 反查导致的“假死等待”
这是 2013 错误最容易被误判的场景。现象是:Navicat 能连上内网数据库,但每次连接要卡 5 到 10 秒,有时直接报 2013;用 IP 连比用域名连成功率高;抓包看到 TCP 开始正常但 MySQL 迟迟不发握手包。
原因在于 MySQL 默认会对每个连接做反向 DNS 解析,把客户端 IP 解析成主机名。如果客户端 IP 在 DNS 服务器上不存在 PTR 记录,或者 DNS 服务器本身响应慢,MySQL 的线程会挂在这里等待,耗尽connect_timeout后主动断开。解决方法是在配置中加入:
[mysqld] skip-name-resolve加上之后 MySQL 不再做反向解析,连接建立会快得多。代价是SHOW PROCESSLIST里看到的Host列将只显示 IP 而不是主机名,掉进user表里的host字段如果写的是主机名也会匹配不上——授权时记得用 IP 或通配符。如果你的user表里有大量基于主机名的授权记录,需要先把它们改成 IP 形式,否则加了这个参数后所有远程连接都会因为匹配不到账号而被拒绝。
3.4 参数清单与重载方式:改完不生效的常见原因
MySQL 参数表(按本场景排查优先级排列):
| 参数 | 作用 | 建议值 | 生效方式 |
|---|---|---|---|
bind-address | 监听接口范围 | 0.0.0.0或具体内网 IP | 重启 |
connect_timeout | 等待客户端首个包的秒数 | 10~20 | 动态生效 |
back_log | 连接队列上限 | 根据并发调整 | 重启 |
skip-name-resolve | 是否做反向 DNS 解析 | 建议开启 | 重启 |
wait_timeout | 非交互空闲连接关闭时间 | 默认即可 | 动态生效 |
interactive_timeout | 交互式空闲连接关闭时间 | 7200 左右(2小时) | 动态生效 |
动态参数可以不用重启直接验证效果:
-- 在 MySQL 命令行执行,无需重启 SET GLOBAL connect_timeout = 20; -- 确认当前值 SHOW VARIABLES LIKE 'connect_timeout';没有写进配置文件的话,重启服务后SET GLOBAL设置会丢,所以验证有效后记得落盘到my.cnf。connect_timeout是少数同时支持全局和会话级设置的参数,排查时可以直接改全局值,不用等连接断开重连。
4. Navicat 侧的处理:连接选项、SSH 通道与客户端参数
4.1 Navicat 连接编辑框里的两个关键设置
Navicat 连接设置里有两个和 2013 直接相关的选项:一个是「连接超时」(或「等待连接超时」),另一个是「保持连接间隔」。前者是 Navicat 在建立 TCP 连接时的等待时间,后者是空闲时定期发送探测包防止连接被中间设备回收。
在 Navicat 的连接属性里把「连接超时」从默认的 10 秒调大到 20~30 秒,能缓解部分网络高延迟场景下的 2013。但注意:这个参数只控制“建立连接”阶段的等待时间,不改 MySQL 服务端的connect_timeout。如果服务端的connect_timeout小于 Navicat 的连接超时,实际生效的是服务端那个较小的值,调 Navicat 侧无效。
「保持连接间隔」建议设置 30 到 60 秒。MySQL 默认wait_timeout是 8 小时,但很多云数据库或公司内部中间件会把超时改小——我们环境里遇到过 300 秒甚至 60 秒的配置。Navicat 每隔一段时间发一个轻量查询保持连接活跃,就不会触发服务端空闲回收。
Windows 下还有一个坑:netsh对 TCP 连接的保活设置默认值偏大,它控制的是系统层面 TCP keepalive 的探测时间,默认 2 小时才发第一个探测包。Navicat 如果依赖系统保活机制,空闲连接很容易被中间防火墙先回收:
# Windows 下把 TCP keepalive 探测时间调为 60 秒(需要管理员权限) netsh interface ipv4 set subinterface "以太网" store=active mtu=1500这条命令本身不是改 keepalive 的——实际上 Windows 的 TCP keepalive 参数要通过注册表或Set-NetTCPSetting调整。比较省事的做法是直接用 Navicat 的「保持连接间隔」功能,它会在应用层发 ping 包,比你调系统内核参数可靠得多。
4.2 网络策略挡死 3306 时的备用路径:SSH 通道
很多线上环境里,3306 端口在边界防火墙就被规则拦掉了,无论你申请白名单还是改 MySQL 配置都无法直接暴露。Navicat 支持 SSH 隧道,把数据库连接封装在 SSH 会话里,这是我在某公司遇到跨部门数据库访问时最常用的方案。
操作步骤:Navicat 新建连接时选择「SSH」标签页,勾选“使用 SSH 隧道”,填入跳板机地址、端口、用户名和认证方式。数据库连接的主机地址填127.0.0.1,端口填3306——因为 SSH 隧道已经把远程 MySQL 端口映射到了本地回环。
SSH 通道配置的常见参数: 主机:跳板机 IP(通常为 22 端口) 用户名:跳板机登录账号 认证:密码或私钥文件 数据库主机:127.0.0.1(隧道已映射) 数据库端口:3306SSH 通道解决的不只是防火墙问题——它还顺带免掉了 MySQL 侧对公网暴露的顾虑,因为 MySQL 可以继续只监听127.0.0.1,SSH 服务负责对外。这个方案对网络抖动也有一定缓冲,SSH 自带 TCP 保活机制,连接不容易被中间设备静默回收。代价是链路多一跳,延迟稍高,但稳定性通常比裸连 3306 好。
用 SSH 隧道后还有一层好处:任何iptables对 3306 的封锁策略全都不再影响你,因为流量走的是 22 端口。很多公司只放行 22 和 80/443,这种环境下 SSH 隧道几乎是唯一干净的解法。
4.3 版本兼容性:Navicat 连 MySQL 8 时的握手差异
MySQL 8 默认认证插件是caching_sha2_password,老版本 Navicat(通常指 15 以下)跟它存在兼容问题。严格说这报的是 1251 错误,但很多人在搜索 2013 时会把两者混在一起看。表现特征是:连接时能正常建立 TCP 和握手,但认证阶段出错或者卡住。
如果你是 MySQL 8 环境,在 Navicat 老版本上遇到奇怪的连接问题,先确认两件事:一是 Navicat 版本是否为较新的 16 或以上;二是 MySQL 侧账号的认证插件是什么:
-- 查看账号使用的认证插件 SELECT user, host, plugin FROM mysql.user WHERE user = '你的用户名';如果插件是caching_sha2_password,但 Navicat 又不支持,有两种解决思路:把my.cnf里default_authentication_plugin改回mysql_native_password(注意 MySQL 8.4 里这个插件可能已被标记废弃,需评估再动);或者更新 Navicat。我们的经验是优先更新客户端,改服务端默认插件会影响所有账号的安全强度,属于比较大的变更。
5. 避坑:2013 错误排查中常见的 5 个坑
5.1 改完 MySQL 配置不重启,白改半天
现象:按网上的教程改了my.cnf里的connect_timeout、bind-address,用SHOW VARIABLES查询发现值没变,连接依然报 2013。
原因:bind-address和back_log属于启动时读取的参数,不重读配置文件就不会生效。很多人只改了文件忘了重启,还以为配置没写对。
解决:修改bind-address这类静态参数后务必备份并重启 MySQL。动态参数用SET GLOBAL生效后会立即作用于新连接,但重启后丢失,所以两件事都要做——先动态改验证效果,再把配置文件改成相同值,最后重启。
5.2 同时改了 skip-name-resolve 和基于主机名的授权,连不上了
现象:加上skip-name-resolve后,原本能连上的账号全部报Access denied,Navicat 换个报错继续,让人误以为 2013 没解决。
原因:MySQL 的user表里授权记录写的是主机名(如'myuser'@'web-server'),开启skip-name-resolve后服务端不再解析主机名,连接来源 IP 无法匹配到任何账号。
解决:开启这个参数之前,先确认user表里的host字段都是 IP、网段或%:
-- 查看是否有基于主机名的授权记录 SELECT user, host FROM mysql.user; -- 如果存在 'web-server' 这类主机名,改成对应的 IP RENAME USER 'myuser'@'web-server' TO 'myuser'@'192.168.1.%'; FLUSH PRIVILEGES;没有RENAME USER权限的环境先CREATE USER新账号再DROP旧账号。
5.3 防火墙 conntrack 表满,新连接全被丢
现象:服务器运行一段时间后突然出现大量 2013,重启 MySQL 也没用;dmesg里看到nf_conntrack: table full, dropping packet,nc测试时 TCP 连接超时。
原因:Linux 连接跟踪表(nf_conntrack)默认大小有限,当并发连接数或短连接数超过上限,新连接的第一个包会被内核直接丢弃,客户端侧表现就是连接卡住直到超时,与 2013 完全一致。
解决:
# 临时调大连接跟踪表 sysctl -w net.netfilter.nf_conntrack_max=131072 # 查看当前使用量与上限 cat /proc/sys/net/netfilter/nf_conntrack_count这种问题通常是业务侧产生大量短连接或连接未正常释放导致的,调整容器参数只是治标。长期做法是排查连接泄漏:ss -s看timewait和established数量,若timewait异常高,说明客户端或连接池没有复用连接。
5.4 服务器负载过高导致握手包延迟超过 connect_timeout
现象:MySQL 服务器 CPU 被打满或 IO 等待很高时,Navicat 连接要么卡住,要么偶发 2013。网络是通的,抓包看到SYN和SYN-ACK都正常,但 MySQL 应用层握手包迟迟不出来。
原因:MySQL 收到 TCP 连接后,需要在用户态线程里发送握手包。如果 MySQL 线程调度被拖延——比如大量慢查询占满 CPU、磁盘 IO 排队——从网络收到连接事件到发送握手包之间的延迟可能超过connect_timeout,服务端主动断开连接。
解决:调大connect_timeout能缓解症状,但根本问题是负载。先看SHOW PROCESSLIST里有什么在跑,确认是不是有慢查询在拖累系统。紧急情况下可以把connect_timeout临时调到 30 秒顶住,然后排查慢查询——这种场景下别把时间花在调优网络参数上,治标不治本。
5.5 中间防火墙对空闲连接静默回收
现象:Navicat 刚连上一切正常,挂机几分钟后回来一操作就报 2013,重新连接又能用。看起来像随机问题,但频率和设备空闲时长强相关。
原因:网络路径上的防火墙或负载均衡设备出于资源管理考虑,会对长时间空闲的 TCP 连接发送RST或直接删除连接状态,且不通知两端。MySQL 服务端和 Navicat 都不知道连接已经失效,下次发送查询时发现连接已死。
解决:在 Navicat 里设置「保持连接间隔」为 30 秒,让空闲连接持续有数据流动。同时检查 MySQL 侧有没有比wait_timeout更小的中间层回收规则——某些云数据库或自建的连接代理产品会在 60 秒到 5 分钟之间回收空闲连接,这个值你直接在 MySQL 上是看不见的,只能靠测试确认。
6. 进阶验证:把 2013 从“玄学”变成可复现的排查流程
6.1 一次 tcpdump 抓包完整还原整个握手过程
2013 错误最令人头疼的地方在于它的偶发性。同样的 Navicat、同样的网络,有时候能连上,有时候报错。固定复现路径是排查的第一步:打开 tcpdump,保存完整数据,对比正常连接和失败连接在握手阶段的差异。
# 分别抓两次连接,保存为文件再比对 tcpdump -i eth0 -n -s 0 -w /tmp/mysql_success.pcap host 192.168.1.50 and port 3306 # 失败后按 Ctrl+C 停止,再抓一次失败场景 tcpdump -i eth0 -n -s 0 -w /tmp/mysql_fail.pcap host 192.168.1.50 and port 3306用 Wireshark 打开两个 pcap 文件,直接看 MySQL 协议层的 Server Greeting 包:成功场景里 TCP 三次握手完成后对端会立刻发来一个包含版本号的握手包;失败场景里你只看到三次握手完成的ACK,然后什么都没有,直到客户端发出FIN或RST。这一行为就能定位问题在“服务端没发包”而不是“包被网络丢了”——前者查 MySQL 进程,后者查防火墙和链路。
如果两次抓包都有 Server Greeting 包,但 Navicat 还是报 2013,那就看 tcpdump 的时间戳:Greeting 包和客户端下一个包之间如果隔着很大的间隔,可能是客户端的 TLS 握手或认证插件协商阶段超时了。MySQL 8 默认开启 TLS 支持时,握手包之后客户端要启动 TLS 协商,这一阶段同样可能被防火墙策略干扰。
6.2 用 MySQL 错误日志确认连接线程的死亡原因
抓包只能告诉你“包没到”,不告诉你“MySQL 为什么不发”。把 MySQL 的错误日志打开,临时提高日志级别,能看到更接近根因的线索:
[mysqld] # 记录所有连接相关的断开和异常信息 log_error_verbosity = 3# 日志位置各发行版不同,通常在 /var/log/mysql/error.log tail -f /var/log/mysql/error.log错误日志里如果出现Aborted connection ... Got an error reading communication packets,说明 MySQL 服务端在等待客户端数据时遇到了异常。Aborted connection的计数太高基本可以确定是网络层在丢弃数据包,而不是服务端本身的问题。配合SHOW GLOBAL STATUS LIKE 'Aborted%';可以对比正常时段和故障时段的差异。
我自己的排查习惯是:每次都把 tcpdump 结果、MySQL 错误日志和SHOW GLOBAL STATUS三个数据源的输出放在一起比对。2013 这种问题单看任何一个都容易被误导——比如错误日志里全是Aborted connection,看起来像客户端断连,但 tcpdump 会告诉你这些连接在握手阶段就被服务端掐断了,根因又绕回connect_timeout或 DNS 反查。三份数据一交叉,问题范围立刻缩小一半以上。
这个流程跑过一次之后,2013 就不再是玄学了。现在每次我遇到同类问题,第一反应永远是抓包看时间线,而不是先去改参数——因为多数情况下参数只是背锅的。希望这套排查路径对你有用,下次再碰到,至少知道从哪下手了。
本文还有配套的精品资源,点击获取