ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL 8连接失败排查思路与实战指南

MySQL 8连接失败排查思路与实战指南 这阵子被问得最多的一个技术问题就是 Mysql8 连接失败。注意不是“MySQL 怎么安装”也不是“SQL 怎么写”而是很具体的——客户端一敲回车就报错有时候是 Access denied有时候是 Cant connect还有的时候报一个根本看不懂的 2059。同样叫“连接失败”背后的原因可能差着十万八千里。这篇文章我想把我处理这类问题的完整思路写下来报错怎么读、该先查什么再查什么、每个环节有哪些在 MySQL 8 上特有的坑。不论你是刚装好数据库的开发机还是线上库出问题的运维按这个顺序走一遍基本都能找到病根。1. 先把报错翻译成人话不同失败现象对应的病灶连接失败这四个字太笼统了你至少得先搞清楚自己是哪一种失败。我的习惯是让求助者把报错原文发过来因为报错文本里基本已经写明了病根在哪一层。很多新手会直接搜“Mysql8 连接失败怎么办”结果搜到一堆答案也不知道该用哪个就是因为没先做这一步分类。下面这张表我整理过无数次每次排查前都会让同事先对号入座报错关键字真实含义主要排查方向ERROR 2003 ... Cant connect ... (10060) 或 Connection timed out客户端发包过去石沉大海防火墙拦截、安全组没放行、网络路由不通ERROR 2003 ... Cant connect ... (10061) 或 Connection refused目标端口没有服务在监听MySQL 服务没启动、端口配错、监听地址不对ERROR 1045 (28000) Access denied for user账号或密码校验失败密码错误、host 不匹配、认证插件问题ERROR 2059 Authentication plugin caching_sha2_password cannot be loaded客户端不认识 MySQL 8 的默认认证插件客户端/驱动版本过旧或需要修改认证方式ERROR 1130 Host ... is not allowed to connect账号授权里没有这个来源主机用户授权 host 范围不对ERROR 1040 Too many connections连接数打满了max_connections 参数、空闲连接占用Lost connection to MySQL server during query连接中途断开网络不稳定、wait_timeout 过短、包太大被中途掐断这个表格不是让你背的是让你在报错出现的那一刻能快速定位到自己该处理哪一层。我自己的排查顺序永远是固定的服务层 → 网络层 → 认证与授权层 → 参数层。这个顺序的本质是从近到远、从确定到不确定。你先在数据库服务器本机用 mysql 客户端连一次如果本机都连不上那防火墙、远程授权这些根本不用看问题大概率出在服务本身或者密码上。如果本机能连、远程连不上那才需要往网络层和授权层走。我见过太多人一上来就问“远程连不上是不是要改 bind-address”结果我让他先本机连一下报的是 Access denied——根本不是 bind-address 的事是密码记错了。报错读不懂排查就会做很多无用功。2. 服务层MySQL 8 压根没跑起来后面全是白搭先讲最基础也最阴险的一层服务本身没起来或者起来了但没在正常监听。MySQL 8 的安装方式和配置逻辑跟 5.7 有区别几个经典坑必须单独拎出来说。2.1 安装方式不同踩坑点也不同如果你是用的官方 MSI 安装包Windows或者 apt/yum 装的Linux一般服务会自动注册好。但很多人喜欢用免安装的 zip 包热词里那个“mysql8免安装板下载”就是指这个这时候最容易出问题的就是解压完之后根本没有 data 目录也没有初始化过。zip 包的正确起手式是解压到目标目录比如D:\mysql-8.0.x-winx64在根目录写一个my.ini用管理员权限打开命令行执行mysqld --initialize或mysqld --initialize-insecure执行mysqld --install注册 Windows 服务net start mysql启动服务这里有个非常多人踩的点用--initialize初始化MySQL 8 会生成一个随机临时密码并把这个密码写到错误日志文件里而不是像 5.7 某些版本那样直接显示在控制台。如果你没看日志就直接拿空密码去连永远会报 1045。第二个坑是mysqld --install之前不先初始化服务能装上但启动必失败因为找不到数据目录。我个人在实际排查时会建议如果只是为了快速验证可以用mysqld --initialize-insecure这样 root 初始密码是空的登录后再改。生产环境绝对不要这么干但排查阶段能少踩一个“日志文件在哪”的坑。2.2 启动失败与日志定位如果服务压根起不来你查配置、查防火墙都是白费力气。Windows 下判断服务状态第一件事是services.msc里看 MySQL 服务是不是“正在运行”或者命令行net start mysql看报什么错。Linux 下用systemctl status mysqld或service mysql status。启动失败最常见的几个原因我按出现频率排一下data 目录有问题初始化失败、目录权限不对Linux 下特别常见data 目录属主不是 mysql 用户、datadir 路径配置指向了不存在的位置。my.ini 写错了MySQL 8 对参数校验更严格不认识或写错的参数可能直接导致启动失败。Windows 下用 Notepad 保存 my.ini 时如果编码是 UTF-8 无 BOM个别版本解析会出现问题保险起见用 UTF-8 with BOM 或 ANSI 保存。端口被占用3306 被其他程序占了服务启动会失败或者起来后监听失败。日志文件路径无权限MySQL 8 启动时要写 error log如果日志目录不可写也会直接退出。定位启动失败唯一靠谱的办法是看错误日志。Windows 下错误日志默认在数据目录下文件名是主机名.errLinux 下常见的是/var/log/mysqld.log或/var/log/mysql/error.log。学会看日志比猜配置高效一万倍日志最后一行基本就是原因。比如我见过有人说“我 MySQL 8 起不来”一查日志是[ERROR] [MY-010262] Cant create/write to file C:\ProgramData\MySQL\MySQL Server 8.0\Data\mysql.ibd——权限问题加上对 ProgramData 目录的写入权限就好了。2.3 端口监听与启动验证服务起来了不代表万事大吉还得确认它确实监听了 TCP 3306。Linux 用ss -tlnp | grep 3306Windows 用netstat -ano | findstr 3306。如果输出只有127.0.0.1:3306说明只在回环地址上监听远程肯定连不上。如果输出是0.0.0.0:3306或*:3306那才说明对所有网卡开放。还有一种情况服务启动了、端口也在监听但你本地用mysql -uroot -p连的时候报 2003 10061。这种大概率是你连的端口和实际端口不一致。默认 3306但有些环境会改到 3307、3308或者你的连接命令把参数写错了。用mysql -h127.0.0.1 -P3307 -uroot -p显式指定端口再试一次基本能排除这个坑。服务层验证动作总结起来就三条进程在不在、日志有没有报错、端口有没有监听。这三条过了再往下走网络层。3. 网络层连接超时大概率不是 MySQL 的问题网络层的报错最典型的就是 10060连接超时和 10061拒绝连接。很多人一看到 10060 就去改 MySQL 配置其实这个方向完全错了。它俩的区别我打个比方10061 像是你敲了门房子是空的没人给你开——这是服务没起或者端口没监听10060 像是你敲了门但根本没人应门甚至敲门声都没传进去——这是中间有堵墙把包丢了也就是防火墙或网络路由问题。3.1 bind-address 与端口监听MySQL 8 的bind_address参数默认是*意思是在所有网卡上监听。但如果配置文件里被改成了127.0.0.1那不管你怎么开放防火墙远程都连不上因为 MySQL 压根不监听外部网卡。查看方式SHOW VARIABLES LIKE bind_address;如果确实是127.0.0.1改 my.ini 里的配置[mysqld] bind-address 0.0.0.0改完重启 MySQL 服务。注意还有另一个参数叫skip-networking如果被设置为 ONMySQL 会完全禁用 TCP/IP只允许本机通过 socket 连接这个一开远程必失败。用SHOW VARIABLES LIKE skip_networking;查看当前主流版本输出应该是OFF。3.2 防火墙、安全组和虚拟机网络模式在网络层排查顺序一定是先确认 MySQL 监听在对外地址再确认服务器本机防火墙放行了 3306再确认云平台安全组放行了 3306最后确认客户端到服务器之间的路由通不通。Linux 防火墙常用 firewalldsystemctl status firewalld临时放行可以用firewall-cmd --add-port3306/tcp永久生效加--permanent参数。Windows 防火墙在“高级设置”里新建入站规则开放 3306或者更暴力一点直接netsh advfirewall firewall add rule namemysql3306 dirin actionallow protocolTCP localport3306。云服务器安全组这是现在最大的坑。你在 ECS 上把 MySQL 的 bind-address 改成 0.0.0.0本机防火墙也关了但控制台安全组的入站规则没加 3306那外部照样连不上现象就是连接超时而不是拒绝连接。因为安全组是在网络入口就把包丢了MySQL 根本没收到。还有一个场景是虚拟机也就是热词里那个“虚拟机出五国后服务器连接失败”的类似环境。VMware 的 NAT 模式下虚拟机可以访问外网但宿主机访问虚拟机的 IP 经常不通因为宿主机不在 NAT 那个子网里。你该做的是给虚拟机配桥接模式或者做端口转发而不是纠结 MySQL 配置。容器环境同理docker run -p 3306:3306之后要用宿主机 IP 而不是容器内部 IP 去连。3.3 端口探测的完整命令序列判断网络层通不通不要用 ping——MySQL 是 TCP 服务只用 ICMP 探活没有任何意义。正确姿势telnet 192.168.1.10 3306Windows 下如果提示 telnet 不是内部命令可以在“启用或关闭 Windows 功能”里勾选 Telnet 客户端或者用下面这个替代curl -v telnet://192.168.1.10:3306Linux 下还可以用 ncnc -vz 192.168.1.10 3306反馈结果怎么解读如果端口开着MySQL 会立刻发一个带版本号的握手包你看版本号就知道服务确实在如果显示Connection refused说明 TCP 层能到达但端口没人监听如果是Connection timed out基本可以断定防火墙或安全组在丢包这时候回去检查防火墙和安全组别在 MySQL 配置上浪费时间。我自己处理线上问题时会在服务器本机、同网段另一台机器、外部网络三个位置依次做 telnet。本机通、同网段通、外部不通——问题在云安全组本机和同网段通、外部通、但客户端通不了——问题在客户端本机防火墙或网络只有本机通——问题在服务器防火墙或监听地址。4. 认证层8.0 默认认证插件引发的握手失败这一节是 MySQL 8 连接失败里最具有“版本特色”的一类报错是 Error 2059具体内容类似Authentication plugin caching_sha2_password cannot be loaded。老玩家可能没见过这个错因为 MySQL 5.7 时代默认认证插件是mysql_native_password几乎不存在这个握手问题。4.1 为什么 MySQL 8 特别容易触发兼容问题MySQL 8.0 从版本 8.0.4 开始把默认认证插件换成了caching_sha2_password。官方换这个插件是因为老协议用的 SHA-1 哈希存在安全短板新插件用 SHA-256 做密码散列配合 RSA 公钥交换和 TLS安全性高了一个等级。它还带一个缓存机制如果同一用户之前认证过服务端会把结果缓存起来后续连接可以用更轻量的挑战-响应流程走完所以性能并不差。问题出在哪认证插件是客户端和服务端“协商”的如果你的客户端工具或驱动程序只实现了mysql_native_password的协议它遇到caching_sha2_password就懵了握手失败。常见的触发场景老版本 Navicat、SQLyog、Workbench 的早期版本。老版 JDBC 驱动Connector/J 8.0.9 版本之前的。老版 Python 库如 PyMySQL 较老版本、mysqlclient 老版本PyMySQL 新版虽然支持 caching_sha2但需要额外安装cryptography依赖。PHP 老版本的 mysqli/pdo_mysql 扩展。4.2 按客户端类型给出对应解法先说我推荐的方向升级客户端而不是反向降级 MySQL 的认证插件。因为 MySQL 8 官方在 8.0.34 之后明确把mysql_native_password标记为废弃到了更新的版本比如 8.4默认禁用你要是为了兼容老客户端而长期用老插件等于给自己埋雷。但现实里不是所有环境都能立刻升级客户端。所以分两种情况处理情况一客户端能升级或增加连接参数这是首选。JDBC 连接串上要补两个参数jdbc:mysql://192.168.1.10:3306/testdb?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue很关键因为 caching_sha2_password 在非 TLS 连接下需要从服务端获取 RSA 公钥来加密密码传输客户端出于安全考虑默认不允许自动获取公钥就会卡在认证阶段。PyMySQL 则可以这样import pymysql conn pymysql.connect( host192.168.1.10, userapp, passwordyourpass, databasetestdb, # 新版 PyMySQL 已经支持 caching_sha2_password但需要 cryptography )如果连接时报错提示缺 cryptographypip install cryptography即可。情况二客户端短时间内无法升级那就只能把对应用户改回老插件。在 MySQL 里执行ALTER USER app% IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;改完用SELECT user, host, plugin FROM mysql.user;验证一下确认 app 的 plugin 变成了mysql_native_password。这个方案在 8.0 各小版本都能用但只能当临时手段。4.3 创建新用户时就指定认证插件如果数据库刚初始化、还没有业务用户建议在创建用户时直接明确插件省得后面排查CREATE USER app% IDENTIFIED WITH caching_sha2_password BY 强密码; GRANT ALL PRIVILEGES ON testdb.* TO app%;这个办法的好处是你可以只给老客户端用的账号保留 mysql_native_password其他新账号全部走新插件两边都不耽误。我自己的经验是一个 MySQL 8 实例上同时存在两种插件的账号是完全没问题的别被“必须全局统一”的错觉束缚住。还有一个注意点caching_sha2_password 的首次认证需要传输明文密码或公钥加密后的密码如果你同时强制要求 SSL 而客户端这边 SSL 配置又不当可能会出现 SSL 握手失败。排查连接问题的时候可以在 JDBC 里先把useSSLfalse加上确认能连上之后再恢复 SSL 配置这样能把认证问题和加密传输问题拆开。5. 权限与参数能连却进不去、连上就被踢的隐藏坑过了前面几关TCP 能通、握手也没问题但报 1045 或 1130那就是权限和授权表的问题了。这一层很考验细心程度因为坑非常隐蔽。5.1 Access denied 与 host 授权1045 的完整报错是ERROR 1045 (28000): Access denied for user app192.168.1.20。这里面有两个信息用户名和来源 IP。常见的失败原因有三类第一类密码确实错了。这个不用多说但是要提醒一点MySQL 8 的密码策略默认偏强如果前面装过 validate_password 组件密码设置得太简单会直接拒绝创建或修改用户。第二类账号存在但 host 不匹配。MySQL 用户是由user host两个维度组成的applocalhost和app%是两个完全不同的账号。如果授权表里只有applocalhost你用 192.168.1.20 这个 IP 来连接服务端计算来源主机时匹配不到行就报 Access denied。很多人以为%能匹配所有主机确实大部分场景下是但有一个非常经典的坑%不匹配通过 socket 进行的本地连接。当你用mysql -uroot -p在本机连接时如果服务器把来源解析成 localhost而授权表里只有root%某些配置下也会失败。反过来也常见授权表里只有rootlocalhost你想用mysql -h192.168.1.10 -uroot远程连接也会失败。所以看到 Access denied 先查这张表SELECT user, host, plugin FROM mysql.user;第三类授权命令本身写错了。最常见的是GRANT ALL PRIVILEGES ON *.* TO app%这行*.*表示所有库但如果你只想授权某个库写成db_name.*。看不出问题的话用SHOW GRANTS FOR app%;查看实际授权。5.2 忘记 root 密码时的重置流程8.0 实测忘记 root 密码也是“连接失败”的重灾区而且 MySQL 8 的重置流程和 5.7 有个细微差别。完整流程我实测过很多次按这个顺序不会出错。Linux 下停掉 MySQLsystemctl stop mysqld以跳过授权表方式启动mysqld_safe --skip-grant-tables --skip-networking 加--skip-networking是为了避免无认证状态下被其他机器连进来安全考虑然后mysql -uroot这时候不需要密码就能进进了之后第一件事不是改密码而是执行FLUSH PRIVILEGES;。为什么因为 MySQL 8 在 skip-grant-tables 模式下授权表是未加载状态你不先 flush 就直接ALTER USER会报ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statementFLUSH 之后再执行ALTER USER rootlocalhost IDENTIFIED BY 新密码;重启 MySQL 恢复正常模式。Windows 下思路一样先net stop mysql然后用命令行手动启动mysqld --skip-grant-tables --console开另一个命令行窗口执行mysql -uroot进入后面的操作完全一致。这里多说一句MySQL 8 的ALTER USER语法要比旧版本严格不指定IDENTIFIED BY可能会踩各种小坑密码最好一次写对因为如果 new password 又不满足 validate_password 的强度要求会报错也不生效。5.3 连接数、超时与 SSL 握手杂项权限没问题但一直连不上或者连上了用一会儿就掉这时候该怀疑参数层了。我按实际遇到的比例来排ERROR 1040 Too many connections是最直观的。用这两个命令看SHOW VARIABLES LIKE max_connections; SHOW STATUS LIKE Max_used_connections;Max_used_connections接近max_connections的话说明连接池被占满了。临时调大可以SET GLOBAL max_connections 500;注意这个参数在不改 my.ini 的情况下重启后就失效了。而且每个连接都会占用线程栈内存无脑调大会把服务器内存吃爆。合理的做法是先排查有没有连接泄漏比如代码里连接没关闭、连接池最小空闲数设得过大。Lost connection to MySQL server during query这个报错在公网环境下非常常见原因很多但几个高概率的wait_timeout或者interactive_timeout太短连接空闲时间一长就被服务端断了网络本身不稳定大查询传输中途超时还有一个是max_allowed_packet太小一次传入的 SQL 语句或结果集超过限制连接会被强制断掉。这个参数默认值是 64MB8.0 开始是 67108864但如果你导数据或者做批量操作经常不够可以适当调到 128MB 或 256MBSET GLOBAL max_allowed_packet 256 * 1024 * 1024;还有一个容易忽略的是connect_timeout这是 MySQL 服务端等待客户端完成握手过程的超时时间默认 10 秒。如果你的客户端连接时服务器要做很多反向 DNS 解析或者网络延迟非常大也可能出现握手超时。不过说实话真正撞上这个坑的人极少大部分“连接超时”还是前面网络层的问题。SSL 相关报错单独提一句因为 MySQL 8 默认会自动生成 SSL 证书并使用加密连接。如果你看到Cannot create SSL connection之类的字样最快速定位的方式是在客户端连接串里先禁用 SSL比如 JDBC 的useSSLfalse如果禁用后能正常连接再回头查证书配置这样就把“能不能连”和“能不能加密连”分开了。6. 最后放一套我常用的最小验证流程这篇文章写得比较长核心是想让你理解“连接失败”不是一个孤立问题而是一条链路。为了让你以后排查时不用回头翻全文我最后把最小验证流程压缩成一套固定动作。我自己每次接手一个“Mysql8 连接失败”的工单都是按这套动作走的基本上五分钟内能定位到是哪一层的问题。第一步在数据库服务器本机执行本地登录mysql -uroot -p如果本机都报 1045说明是密码或账号问题直接进权限层处理如果本机报 2003说明服务有问题去看进程和日志。第二步查监听状态# Linux ss -tlnp | grep 3306 # Windows netstat -ano | findstr 3306确认监听地址是0.0.0.0或*不是127.0.0.1。第三步本机用 TCP 方式再连一次排除 socket 干扰mysql -h127.0.0.1 -P3306 -uroot -p第四步从另一台机器做端口探测telnet IP 3306通 – 网络层没问题refused – 回去查服务timeout – 回去查防火墙和安全组。第五步如果能通但连不上用这个 SQL 检查账号和授权SELECT user, host, plugin FROM mysql.user;把报错里的用户名和来源 IP 跟这张表比对看看是不是 host 不匹配或者 plugin 是 caching_sha2_password 而客户端不支持。第六步如果以上全部正常还是连不上打开 MySQL 错误日志看从客户端发起连接的时间点附近有没有记录。这一步能捕捉到很多没办法从客户端推断的信息比如 DNS 反查超时、连接超限被拒等。这套流程我反复用了好几年在 MySQL 5.7 和 8.0 上都成立。区别在于 8.0 多了 caching_sha2_password 这个变量多了一个排查维度但整体思路完全没变。每次帮人排查我都是先问一个问题“把完整报错文本发我不要只发一张截图”因为报错里的用户、来源 IP、端口信息才是定位的关键。最后再分享一个小技巧如果你实在搞不定不要一直试那个报错界面。换一个客户端工具试。比如 Navicat 连不上用命令行 mysql 试试命令行不行再用 Python 的 PyMySQL 试试。不同的客户端实现细节不一样有时候是驱动版本问题工具一换就知道问题到底在服务端还是客户端省下很多来回验证的时间。我见过不少人折腾了一下午最后发现不是 MySQL 的问题是 Navicat 老版本和 MySQL 8 的认证插件不兼容——换新版本立刻就好。排查连接问题的核心从来不是背命令而是快速缩小范围找到出问题的那一层。
返回列表