尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MySQL 8.0 远程连接:网络、授权与认证插件全链路排查

MySQL 8.0 远程连接:网络、授权与认证插件全链路排查 本地敲mysql -uroot -p一切正常换到另一台机器上用客户端连立马翻脸不认人——报错还五花八门Host 192.168.1.20 is not allowed to connect to this MySQL server、Cant connect to MySQL server on 10.0.0.5 (10061)、由于目标计算机积极拒绝无法连接、Authentication plugin caching_sha2_password cannot be loaded。这几个错误长得像亲戚其实分属完全不同的层次处理顺序搞反了就会在一件事上耗掉整个下午。MySQL 8.0 远程连接之所以年年有人问就是因为它同时压在四层上网络监听、主机防火墙、账号授权rootlocalhost和root%的区别就在这一层、认证插件。任何一层没打通现象都是连不上但修法完全不同。这篇东西写给三类人刚在云服务器或虚拟机里装完 MySQL 8.0、想让本地客户端连上去的开发者用 Docker 起 MySQL 8.0 之后发现端口映射怎么都不通的运维新手以及被Access denied和10061反复折磨、只想快速定位到底是哪一层出问题的人。下面按先分类、再逐层拆、最后给完整排查链路的顺序来每一层都会告诉你为什么这么设计、命令为什么这么敲而不是丢一堆 SQL 让你照抄。1. 先把连不上拆成两类网络根本不通 vs 账号权限不匹配1.1 判别两类问题的分界线在哪连接被拒绝和Host is not allowed to connect这两句话看起来都是失败但含义差得远。前者的意思是客户端发出的 TCP SYN 包被目标主机明确拒绝了或者压根没到达 MySQL 进程——握手都没开始MySQL 连你是谁都不知道。后者恰恰相反说明TCP 连接已经建立成功MySQL 服务端已经完成了握手、拿到了你的来源 IP然后在权限表里查了一圈发现这个来源不在允许列表里主动把连接踢掉了。判断方法其实很简单一句话看报错里有没有出现你客户端的 IP。报错里带172.16.3.88 is not allowed、Access denied for user root172.16.3.88—— 网络层是通的问题在账号授权直接跳到第 3 章。报错是10061、积极拒绝、timed out、2003—— 网络层就不通别去翻权限表先查监听和防火墙看第 2 章和第 5 章。我见过太多人在第二种情况下疯狂改mysql.user表加了三个root%还是连不上最后发现是安全组没放行 3306。方向错了做多少都是无用功。1.2 常见报错文本对应的故障层把常见报错和它真正指向的层次列出来遇到问题先查表能省掉一半时间报错文本含义优先排查层Host x.x.x.x is not allowed to connectTCP 已通来源主机被拒账号授权层rootlocalhost/root%Cant connect to MySQL server on ip (10061)Windows 下典型的 TCP 拒绝监听地址、防火墙、容器端口映射由于目标计算机积极拒绝无法连接。(10061)同上中文系统表述同上重点看端口有没有真的在听Cant connect ... (110) timed out包被静默丢弃安全组、防火墙 DROP 规则Access denied for user rootip (using password: YES)账号在、密码错或来源不匹配授权表、密码、认证插件Access denied ... (using password: NO)客户端没带密码客户端连接配置Authentication plugin caching_sha2_password cannot be loaded客户端版本太老认证插件层第 4 章Host ip is blocked because of many connection errors被max_connect_errors拉黑执行FLUSH HOSTS解封Lost connection ... at reading initial communication packet中间设备干扰或超时中间网络、SSH 转发链路1.3 为什么 MySQL 8.0 让远程连接更容易失败有几个设计变化是老版本 MySQL 用户转到 8.0 之后最容易吃亏的地方需要提前知道默认只监听回环地址。Debian/Ubuntu 的 MySQL 8.0 包安装后bind-address默认是127.0.0.1也就是只有本机能连。这是出于安全考虑的保守默认值但对想远程连的人来说就是第一个坑。默认认证插件换成了caching_sha2_password。5.7 时代是mysql_native_password很多老客户端、老驱动对这个新插件不支持握手阶段就断了报错还特别绕。密码强度策略默认开启。validate_password组件在 8.0 里是默认装的你想给远程账号设个123456会直接被拒绝报ERROR 1819很多人以为是权限不够其实是密码太弱。默认不允许 root 从任意主机登录。安装时创建的只有rootlocalhostroot%根本不存在所以我从远程用 root 连这个动作在 8.0 默认配置下必然失败。把这四点记住后面的排查基本就是填空题。2. bind-address 与监听地址第一个必须过的关卡2.1 MySQL 8.0 默认只监听 127.0.0.1 的原因MySQL 装完之后服务进程在 3306 端口上听的范围是由bind-address决定的。默认值是127.0.0.1意味着内核只会把发往本机回环地址的 3306 流量交给 mysqld外部网卡上来的包直接没人接内核回一个 RST —— 这就是客户端看到的10061 积极拒绝。为什么发行版要这么设因为数据库是最不该默认暴露在公网上的服务之一。历史上大量服务器因为 3306 开放 弱密码被拖库所以官方包的默认策略是宁可你用不了也不默认开。理解了这一点你就知道这个值不是 bug是特性改它的时候心里要有数。2.2 检查当前监听状态的两条命令在动手改配置之前先确认现状别凭猜# 看 3306 到底听在哪个地址上 ss -lntp | grep 3306 # 输出示例未开放远程 # LISTEN 0 151 127.0.0.1:3306 0.0.0.0:* users:((mysqld,pid1234,fd23)) # 输出示例已开放远程 # LISTEN 0 151 *:3306 0.0.0.0:* users:((mysqld,pid1234,fd23))如果看到的是127.0.0.1:3306那不用往下猜了先改配置。如果看到的已经是*:3306或0.0.0.0:3306说明监听没问题去查防火墙和账号。ss比netstat好用的地方在于它不依赖net-tools包新系统上默认就有。另一个必须确认的点是配置里绝对不能有skip-networking这个参数一开mysqld 完全不监听 TCP只走 socket任何远程连接都不可能成功而且日志里不一定有明显提示。2.3 修改 bind-address 的完整操作配置文件的路径在不同发行版上不一样先搞清楚你的 MySQL 到底读的是哪个文件否则改了半天不生效# 让 mysqld 自己告诉你它读了哪些配置 mysqld --verbose --help | grep -A1 Default options are read from # 或者更直接 my_print_defaults mysqld | head -30常见路径对照发行版 / 安装方式主配置文件通常放 bind-address 的位置Debian / Ubuntu apt/etc/mysql/my.cnf/etc/mysql/mysql.conf.d/mysqld.cnfCentOS / RHEL / Rocky/etc/my.cnf/etc/my.cnf.d/mysql-server.cnf通用二进制包解压目录下my.cnf自己决定Docker 官方镜像/etc/my.cnf/etc/mysql/conf.d/*.cnf找到文件后在[mysqld]段里改[mysqld] # 允许所有网卡监听生产环境建议写具体的内网 IP bind-address 0.0.0.0 # 端口保持默认如果改过端口这里要对应 port 3306注意bind-address是静态参数必须重启 mysqld 才生效systemctl reload或mysqladmin reload都改不动它。重启命令用systemctl restart mysqldDebian 系是mysql重启后用ss -lntp | grep 3306再确认一次。从 MySQL 8.0.13 开始bind-address允许写多个地址用逗号分隔比如bind-address 127.0.0.1,10.0.0.5这样可以同时保留本地 socket 连接和内网监听。如果你的版本低于 8.0.13只能写一个地址写了多个会启动失败。这个细节在官方文档里不显眼但我在线上见过因为多地址配置导致 mysqld 起不来的情况。还有一个容易被忽略的点!includedir是按文件名顺序加载的后面加载的文件会覆盖前面的同名参数。如果你的/etc/my.cnf.d/里有两个文件都写了bind-address生效的是排序靠后的那个。改之前先用grep -rn bind-address /etc/mysql/ /etc/my.cnf*全局搜一遍避免改了 A 文件、被 B 文件覆盖。3. rootlocalhost 和 root% 到底差在哪3.1 MySQL 的账号是用户名 来源主机两个字段这是整个问题的核心认知也是最容易被忽略的一点MySQL 里的用户标识不是一个字符串而是用户名主机的组合。rootlocalhost和root%在系统表里是两条完全独立的记录可以有不同的密码、不同的认证插件、不同的权限。你在本地改了 root 的密码改的是rootlocalhost那条远程那条纹丝不动。查看当前有哪些账号SELECT user, host, plugin FROM mysql.user ORDER BY user, host;典型的默认输出是这样的---------------------------------------------------- | user | host | plugin | ---------------------------------------------------- | mysql.infoschema | localhost | caching_sha2_password | | mysql.session | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | | root | localhost | caching_sha2_password | ----------------------------------------------------看到没有只有一个rootlocalhost。你从远程用 root 连来源 IP 是192.168.1.50MySQL 在表里找不到能匹配这个来源的 root 记录直接回Host 192.168.1.50 is not allowed to connect。这不是密码问题是账号根本不存在。3.2 主机匹配的优先级规则以及匿名用户那个大坑理解了两条独立记录下一个问题就是如果同时存在rootlocalhost、root192.168.1.%、root%MySQL 用哪一条规则是按主机匹配的具体程度排序越具体的越优先。大致顺序是字面主机名或完整 IP 带通配符但前缀更长的如192.168.1.% 纯%。所以从192.168.1.50连过来会命中192.168.1.%那条而不是%那条。这里有个历史上非常著名的坑空字符串主机。某些安装方式尤其是通过打包脚本或旧版升级上来的会在mysql.user里留下localhost、%这样的匿名账号。空主机名在匹配规则里的位置排在%之前所以从远程连过来时可能先被匿名账号匹配上然后因为匿名账号没有权限而报Access denied for user 192.168.1.50——注意用户名部分是空的这个特征非常明显。处理办法很直接-- 先确认有没有匿名账号 SELECT user, host FROM mysql.user WHERE user ; -- 有就删掉 DROP USER IF EXISTS localhost; DROP USER IF EXISTS %;另一个相关的坑是max_connect_errors。默认值不小但如果你反复用错误配置试探达到阈值后 MySQL 会把这个来源 IP 直接拉黑之后即使配置全对也会报Host x.x.x.x is blocked because of many connection errors。这时候执行一句FLUSH HOSTS;就能解封不用重启服务。3.3 正确创建远程账号的完整写法想从远程连规范做法是新建一个专用账号而不是把root%放出来。但如果你的场景确实需要 root 远程比如内网测试环境写法是这样-- 创建允许任意主机来源的 root 账号 CREATE USER root% IDENTIFIED BY YourStrongPwd_2024; -- 授予全部权限并且允许它继续给别人授权 GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; -- 查看结果 SELECT user, host, plugin FROM mysql.user WHERE user root;这里有几个细节值得说明。第一CREATE USER和GRANT之后不需要FLUSH PRIVILEGES。网上大量教程会在末尾加一句FLUSH PRIVILEGES;其实只有当你直接用INSERT/UPDATE改mysql.user表时才需要手动刷新权限缓存。用 DDL 语句操作MySQL 会自动同步。加上去不会错但会误导新人以为这是必须的。第二密码强度策略会拦你。8.0 默认装了validate_password组件设弱密码会报ERROR 1819 (HY000): Your password does not satisfy the current policy requirements查看当前策略并临时放宽仅限测试环境生产别这么干SHOW VARIABLES LIKE validate_password%; -- 8.0.4 之后是组件形式变量名带点号 SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 8;注意老教程里写的是validate_password_policy下划线那是 5.7 时代的写法8.0 上执行会报变量不存在。这个差异坑过不少人。第三只想让某个网段连就别写%。更精确的写法CREATE USER app_rw192.168.1.% IDENTIFIED BY AppPwd_2024#x; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_rw192.168.1.%;这样即使密码泄露攻击面也只在一个内网段里。3.4 直接改 rootlocalhost 的主机字段行不行有人会想那我把rootlocalhost的 host 字段直接改成%不就行了技术上可以UPDATE mysql.user SET host % WHERE user root AND host localhost; FLUSH PRIVILEGES;但我强烈不建议。原因有两个一是这么干之后本地 socket 连接可能反而出问题因为rootlocalhost没了本地连接也得走 TCP 匹配%二是你失去了本地一个高权限账号、远程一个受限账号的分层结构。正确姿势是保留rootlocalhost不动另建远程账号这也是 MySQL 官方推荐的做法。4. caching_sha2_password 认证插件MySQL 8.0 最隐蔽的一道坎4.1 认证插件换代的背景MySQL 8.0.4 开始默认认证插件从mysql_native_password换成了caching_sha2_password。换的原因不是心血来潮老插件的密码哈希算法强度已经不够看了新插件基于 SHA-256安全性和抗暴力破解能力都高一截。但它带来了兼容性问题。caching_sha2_password的握手流程更复杂第一次连接时如果没有缓存需要走一次完整的密钥交换RSA 公钥加密密码或者走 TLS 通道。老版本客户端不认识这套流程就会出现各种奇怪报错Authentication plugin caching_sha2_password cannot be loadedPublic Key Retrieval is not allowedJava 驱动常见Client does not support authentication protocol requested by server这个错误的特点是账号、密码、权限全对网络也通就是连不上。很多人在这里卡住反复重设密码也没用因为问题不在密码本身。4.2 三种解决思路的取舍方案做法优点代价升级客户端换新版 Navicat / DBeaver / JDBC 驱动 / PHP 扩展安全性最好不改服务端老系统可能升不动改账号认证插件ALTER USER ... IDENTIFIED WITH mysql_native_password见效最快一行 SQL安全性下降8.4 起该插件默认禁用强制走 TLS服务端配置证书客户端启用 SSL安全且兼容绕开 RSA 交换限制配置证书有工作量我的建议顺序是先试升级客户端升不动再考虑改插件改插件只改必要的那个业务账号别动 root。因为mysql_native_password在 8.0.34 已经标记为废弃8.4 版本默认不再加载现在图省事改过去的账号将来升级大版本时还得再改一遍。4.3 具体命令与验证方式改单个账号的认证插件ALTER USER app_rw192.168.1.% IDENTIFIED WITH mysql_native_password BY AppPwd_2024#x; -- 确认改动生效 SELECT user, host, plugin FROM mysql.user WHERE user app_rw;Java 应用如果用的是 8.0 之前的 JDBC 驱动连接串里加上这两个参数通常能解决Public Key Retrieval is not allowedjdbc:mysql://10.0.0.5:3306/app_db?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai提示allowPublicKeyRetrievaltrue的作用是允许客户端从服务端获取 RSA 公钥来加密密码。它意味着密码加密走的是明文信道上的非对称加密安全性弱于 TLS只建议在内网使用。公网环境请老老实实配 TLS。如果你不想降级插件也不想自签证书还有一个折中办法用 8.0 自带的方式生成自签证书并开启require_secure_transport让所有远程连接强制加密。这个工作量比想象中小mysql_ssl_rsa_setup会自动生成一套。但要注意一旦开了require_secure_transport本地都不允许明文连接得确保所有客户端都支持 SSL否则会把自己锁在外面。5. 防火墙、安全组与 Docker 端口映射网络层最后三关5.1 主机防火墙放行 3306监听改好了账号也建了还是连不上接下来查主机防火墙。不同系统的操作差别不小# firewalldCentOS / RHEL / Rocky firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reload firewall-cmd --list-ports # 只放行指定网段更安全rich rule 写法 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port protocoltcp port3306 accept firewall-cmd --reload # ufwUbuntu ufw allow from 192.168.1.0/24 to any port 3306 proto tcp ufw status verbose用iptables直接写规则也行但要记得持久化iptables-save否则重启就丢。另外注意如果服务器上装了fail2ban之类的工具它可能在你反复试错之后把客户端 IP 加进黑名单报错表现为突然连不上、之前明明能用。排查时先看iptables -L -n完整链别只看某个链。5.2 云主机安全组最容易漏的一层如果机器是云服务器除了系统防火墙还有一层云平台的安全组两者是独立的。很多人改完系统防火墙就以为完事结果还是不通原因就在安全组没放行。安全组排查要点入方向规则里要有TCP 3306源地址写你的固定公网 IP 或内网网段不要图省事写0.0.0.0/0。出方向一般默认全放行但有些严格环境下出方向也被限制需要一并检查。同一账号下的多台机器如果互相访问走内网 IP 通常不受公网安全组限制但受内网 ACL 限制。判断是不是安全组问题有个快速验证法从云主机的浏览器 webshell 或跳板机上执行mysql -h 内网IP -P 3306。内网通、公网不通基本就是安全组或公网映射的问题内网也不通就是监听或账号的问题。5.3 Docker 部署 MySQL 8.0 时的三个典型错误现在越来越多人用容器跑 MySQL 8.0这一层的坑和裸机不太一样错误一端口映射只绑定了回环地址。有些教程为了让数据库更安全会写-p 127.0.0.1:3306:3306。这个写法的意思是宿主机上只有回环地址监听 3306从外面当然连不上。想远程连必须写成-p 3306:3306或-p 0.0.0.0:3306:3306。错误二不知道容器里账号的默认状态。MySQL 官方镜像的启动脚本里MYSQL_ROOT_HOST的默认值就是%也就是说容器启动时会自动建一个root%。所以用容器跑的时候从宿主机之外连不上大概率不是账号问题而是端口映射或防火墙。想限制来源可以显式设置-e MYSQL_ROOT_HOST172.17.0.1。错误三配置文件挂载覆盖了默认监听。有人把宿主机的my.cnf整个挂到/etc/mysql/my.cnf而那份文件里写着bind-address 127.0.0.1结果容器内部只监听回环端口映射出去也是死的。正确做法是只挂一个conf.d目录下的小文件docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRootPwd_2024 \ -v /data/mysql8/conf:/etc/mysql/conf.d \ -v /data/mysql8/data:/var/lib/mysql \ mysql:8.0还有一点必须提醒Docker 会向 iptables 的DOCKER链里插规则这些规则优先于 ufw 的规则。所以经常出现ufw status显示 3306 是 deny但外部偏偏能连上的怪现象。要真正限制容器端口得往DOCKER-USER链里加规则光靠 ufw 是拦不住的。6. 一次完整的排查实录从 10061 到连接成功6.1 现场信息收集五条命令锁定层次假设场景是这样的一台内网 CentOS 服务器MySQL 8.0 刚装好客户端在 Windows 上用 Navicat 连报由于目标计算机积极拒绝无法连接。(10061)。我的固定排查顺序是这样第一步客户端侧确认端口。先把 DNS、代理之类的干扰因素排除掉# Windows PowerShell Test-NetConnection 10.0.0.5 -Port 3306如果TcpTestSucceeded: False说明 TCP 层就不通继续往下如果是True直接跳到第 3 章的账号排查。第二步服务端确认监听。上服务器敲ss -lntp | grep 3306 mysql -h 127.0.0.1 -uroot -p -e SELECT 1;如果ss显示的是127.0.0.1:3306问题定位完成——监听范围不对。如果本地命令行都连不上那问题更基础先看 mysqld 是不是还活着systemctl status mysqld、tail -50 /var/log/mysqld.log。第三步确认监听参数来源。找到实际生效的配置文件grep -rn bind-address\|skip-networking /etc/my.cnf /etc/my.cnf.d/第四步看防火墙。firewall-cmd --list-all第五步账号授权表。这一步在监听修好之后再查SELECT user, host, plugin FROM mysql.user; SHOW VARIABLES LIKE bind_address;6.2 修复过程与验证这个场景的根因通常就一个bind-address 127.0.0.1。修复动作是三步# 1. 改配置 sed -i s/^bind-address.*/bind-address 0.0.0.0/ /etc/my.cnf.d/mysql-server.cnf # 2. 重启bind-address 不支持热加载 systemctl restart mysqld # 3. 确认监听范围变了 ss -lntp | grep 3306看到*:3306之后再从客户端试一次。如果这时候报错从10061变成了Host 192.168.1.50 is not allowed to connect恭喜你说明网络层已经打通问题收敛到了账号层这是好事——从什么都可能变成了只剩一件事。接着建账号CREATE USER app_rw192.168.1.% IDENTIFIED BY AppPwd_2024#x; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_rw192.168.1.%;再从客户端连如果又冒出Authentication plugin caching_sha2_password cannot be loaded说明客户端版本老按第 4 章处理。整个链路走完从 10061 到连接成功实际上可能只改了两行配置、执行了两条 SQL。真正花时间的是判断现在卡在哪一层。6.3 修好之后仍然偶发失败的两类原因有些情况是明明配好了偶尔又连不上这类问题比一次性配错更烦人一是连接数被打满。报错变成Too many connections。查SHOW VARIABLES LIKE max_connections;和SHOW STATUS LIKE Threads_connected;。如果是连接池配置不当导致连接泄漏改服务端参数只是治标得回头查应用侧的连接池。二是wait_timeout导致的空闲断连。默认wait_timeout是 28800 秒但如果被人改小连接池里的空闲连接会被服务端单方面关闭应用拿到一个已死的连接去执行 SQL报MySQL server has gone away或者Communications link failure。这类问题的特征是第一次请求必失败、重试就成功。解决办法是客户端连接池配置心跳检测如 HikariCP 的keepaliveTime要小于服务端wait_timeout。7. 长期做法别用 root 远程账号与安全的最小必要配置7.1 按业务拆账号的授权模板把root%放出去等于把保险柜钥匙挂在门把手上。生产环境的账号应该按谁用、从哪来、干什么三个维度拆开。我常用的模板是这样-- 只读账号给报表和数据分析用 CREATE USER report_ro192.168.1.% IDENTIFIED BY RoPwd_2024#a; GRANT SELECT ON app_db.* TO report_ro192.168.1.%; -- 应用读写账号给服务端程序用 CREATE USER app_rw192.168.1.% IDENTIFIED BY RwPwd_2024#b; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO app_rw192.168.1.%; -- 运维账号只在跳板机段可用 CREATE USER ops_admin192.168.1.10 IDENTIFIED BY OpsPwd_2024#c; GRANT ALL PRIVILEGES ON *.* TO ops_admin192.168.1.10 WITH GRANT OPTION;配置完用SHOW GRANTS FOR app_rw192.168.1.%;逐个复核一遍。这里有个实操心得授权时优先授库级权限不要授全局权限。GRANT SELECT ON app_db.*和GRANT SELECT ON *.*看起来只差一个字符出事时的爆炸半径差好几个数量级。特别是FILE权限有了它能读写服务器上任意文件非必要绝对不能给。7.2 加固清单如果这台库要长期对外提供服务这几件事建议一次性做完加固项具体做法说明换端口port 13306降低被批量扫描命中的概率不是安全措施是降噪限制来源账号 host 写具体网段比%精确得多强制加密require_secure_transport ON所有连接走 TLS关闭本地文件导入local_infile OFF防LOAD DATA LOCAL类攻击开启慢查询日志slow_query_log ON排查性能问题的基础定期审计账号脚本化对比mysql.user及时发现新增的高权限账号关于端口我得说句实话改端口只能挡住自动化扫描挡不住针对性的探测。真正有价值的是限制来源 强制加密这两条。7.3 几个我实际踩过的细节最后分享几个文档里不写、但实操中一定会碰到的点。第一用 SSH 端口转发比直接暴露 3306 省事得多。如果你能 SSH 上那台服务器直接本地执行ssh -N -L 13306:127.0.0.1:3306 user10.0.0.5然后客户端连127.0.0.1:13306就行了。这样 MySQL 完全不用对外开端口bind-address保持127.0.0.1也没关系安全性和便利性兼顾。用 VS Code 远程连服务器开发的同学也可以在 SSH 配置里加LocalForward 13306 127.0.0.1:3306连上服务器后本地自动就能连数据库了比开安全组规则干净得多。第二注意区分127.0.0.1和localhost在客户端侧的含义。有些客户端里localhost会走本地 socketunix socket而不是 TCP尤其在 macOS 和 Linux 上。你想测试远程连接连接主机一定要写明确的 IP写localhost可能测的是本地另一个 MySQL结果自己骗自己。第三云厂商的数据库白名单和服务器安全组是两回事。如果你用的是托管数据库服务控制台里还有一个独立的访问白名单加 IP 的地方可能藏得很深。服务器安全组放行了、数据库白名单没加照样连不上报错同样是超时。第四密码里的特殊字符会要命。命令行里mysql -uapp_rw -pPa$$w0rd会因为 shell 展开$而变成另一个密码导致Access denied。用单引号包起来可以避免大部分问题但更稳妥的是交互式输入密码或者用mysql_config_editor生成的.mylogin.cnf加密登录路径。我见过有人因为密码里有个!被 bash 历史展开成上一条命令排查了半小时。
返回列表