MySQL 8.0远程连接被拒:root@localhost与root@%区别

发布时间:2026/9/18 17:13:21
MySQL 8.0远程连接被拒:root@localhost与root@%区别 凌晨两点被电话叫醒运维群里甩过来一张截图ERROR 2003 (HY000): Cant connect to MySQL server on 10.0.0.12:3306 (111)。服务跑得好好的昨天还能连今天远程就连不上了。这种场面做过后端或者运维的人多少都遇到过尤其是 MySQL 8.0 铺开之后远程连接被拒绝的问题反而比 5.7 时代更常见。原因不复杂但坑埋得很深——很多人的第一反应是去改my.cnf里的bind-address改完重启发现还是连不上然后在网上翻半天看到rootlocalhost和root%这两个东西越看越糊涂。这篇就把 MySQL 8.0 远程连接设置方法从头到尾捋一遍。核心要解决的问题有三个为什么本机连得上、远程连不上rootlocalhost和root%到底有什么区别该建哪个以及mysql connection refused、Access denied、Authentication plugin cannot be loaded这些报错分别对应哪一层的问题。适合刚接触 MySQL 8.0 的开发、正在用 Docker 装 MySQL 8.0 的人也适合被线上连接问题折腾过的运维。我会按先分层定位、再动手改、最后验证的顺序讲中间穿插我自己踩过的坑和几个容易被忽略的参数。2. 吃透账号模型rootlocalhost 与 root% 不是同一个账号2.1 MySQL 的账号是用户名 来源主机的组合先把最容易误解的一点讲清楚。MySQL 里的账号不是一个用户名而是一个二元组用户名来源主机。rootlocalhost和root%在 MySQL 眼里是两条完全独立的记录有各自的密码、各自的权限、各自的认证插件彼此之间没有任何继承关系。这就解释了很多人遇到的诡异现象你明明在本机终端敲mysql -uroot -p能进去密码也没错但换一台机器用同样的账号密码就报Access denied for user root10.0.0.5。注意报错里的主机部分——root10.0.0.5也就是说客户端从 10.0.0.5 发起的连接MySQL 拿这个来源地址去匹配账号表匹配到的不是rootlocalhost而是一个不存在的记录直接被拒。MySQL 8.0 全新安装后默认只创建一条 root 记录host 部分是localhost含义是只接受来自本机 socket 或 127.0.0.1 的连接。这时候你不管怎么改防火墙、怎么改监听地址只要没给root%或者带具体网段的记录授权远程就永远进不来。匹配规则上MySQL 不是按最精确优先随便挑的它有一套排序逻辑host 越具体的优先级越高。具体主机名或 IP 比%优先localhost比%优先。所以当rootlocalhost和root%同时存在时本机连接走的是localhost那条远程连接走的是%那条。两条记录密码不一样的时候你会看到本机这个密码能登远程同一个密码登不上这种让人怀疑人生的现象——其实它们本来就不是一个密码。2.2 一条命令行验证当前到底有哪些账号排查这类问题第一步永远是先看清楚现状。登进数据库后执行SELECT user, host, plugin FROM mysql.user ORDER BY user, host;你会看到类似这样的结果userhostpluginrootlocalhostcaching_sha2_passwordmysql.infoschemalocalhostcaching_sha2_passwordmysql.sessionlocalhostcaching_sha2_passwordmysql.syslocalhostcaching_sha2_password如果这张表里找不到任何 host 为%或者带具体网段的 root 记录远程连不上就是必然的后面那些防火墙、监听地址的检查可以先放一放。这里插一句MySQL 8.0 用的是 InnoDB 引擎的系统表权限表已经不再是 MyISAM所以mysql.user是能直接查的表不像早年有些版本会建议用SHOW GRANTS绕开。但要注意authentication_string字段别乱改8.0 的密码散列格式和 5.7 不兼容手工拼接字符串改密码大概率会把自己锁在门外。2.3 host 字段写 % 和写具体网段的差别%是通配符代表任意来源主机。听起来很方便实际上在生产环境里是个隐患。假设你有一台数据库业务服务器都在 192.168.1.0/24 网段那更稳妥的写法是CREATE USER app192.168.1.% IDENTIFIED BY StrongPass!2024;这样只有这个网段的机器能尝试连接跨网段的扫描流量连账号匹配这一关都过不去。反过来如果你直接开root%理论上任何能访问到 3306 端口的地址都可以发起密码爆破风险高很多。还有一点%匹配不到localhost的 socket 连接。有人图省事把rootlocalhost删了只留root%结果发现本机用 socket 也连不上了或者行为变得很怪。原因在于localhost在 MySQL 里有特殊含义走的是 Unix socket 而非 TCP匹配规则上不归%管。所以正确做法是保留rootlocalhost作为本地维护通道另外单独建远程账号。3. 排查顺序把连不上拆成四个可独立验证的层3.1 从 TCP 握手开始逐层往上排除远程连接失败的报错看着五花八门其实对应的层次是很清晰的。我习惯按下面的顺序走每层都有明确的验证命令一次只改一个变量层次典型报错快速验证方式网络可达性Connection timed out、No route to hostping、telnet ip 3306端口监听Connection refused (111)ss -lntp | grep 3306账号匹配Access denied for user xipSELECT user,host FROM mysql.user认证方式Authentication plugin caching_sha2_password cannot be loaded查plugin字段权限范围Access denied for ... to databaseSHOW GRANTS FOR xip连接数Too many connectionsSHOW STATUS LIKE Threads_connected重点看报错里的关键字。Connection refused和Connection timed out完全是两回事前者是端口没人监听或者被防火墙直接拒绝TCP 层给了 RST后者是包发出去没回应通常是安全组或者中间设备静默丢包。Access denied则说明 TCP 已经通了MySQL 服务也响应了问题出在账号层。分清楚这一点能省掉一大半瞎折腾的时间。3.2 检查监听地址bind-address 决定服务听哪个网卡MySQL 服务端有个bind-address参数控制它监听哪个网络接口。默认配置里常见的是bind-address 127.0.0.1这个配置的含义是只监听回环地址外面来的连接根本到不了 MySQL 进程表现就是Connection refused。改成0.0.0.0表示监听所有网卡bind-address 0.0.0.0改完必须重启服务生效。不过这里有个前提要说清楚把bind-address开成0.0.0.0是让服务愿意接受外部连接不等于允许外部连接。账号授权、防火墙、云平台安全组任何一层没放行照样连不上。很多人改了bind-address就以为万事大吉其实只是过了第一关。查找配置文件位置可以用mysql --help | grep -A1 Default options或者直接看服务启动参数ps aux | grep mysqldss命令验证监听状态更直观ss -lntp | grep 3306如果输出里是127.0.0.1:3306说明只监听了本机如果是0.0.0.0:3306或:::3306说明已经对外监听。这个输出比看配置文件可靠因为配置文件可能有多份实际生效的只有一个。注意配置文件有加载顺序/etc/my.cnf、/etc/mysql/my.cnf、/etc/mysql/mysql.conf.d/mysqld.cnf都可能存在后加载的会覆盖前面的。改完一定要用SHOW VARIABLES LIKE bind_address;确认实际生效值别改了一个被忽略的文件还在那儿纳闷。3.3 用实际命令确认防火墙和安全组系统防火墙这块firewalld和ufw两套体系都要会看。CentOS 系常见的是firewall-cmd --list-ports firewall-cmd --permanent --add-port3306/tcp firewall-cmd --reloadUbuntu 系ufw status ufw allow 3306/tcp如果是云主机还有一层安全组在控制台里配命令行看不到。这一步经常被忽略尤其是本地测试通了、上云就连不上八成是安全组没开。安全组放行建议只放业务服务器的具体 IP别图省事开0.0.0.0/03306 暴露在公网被扫到是分分钟的事。验证端口通不通最土也最有效的办法是telnettelnet 10.0.0.12 3306连上会显示一段 MySQL 的握手信息连不上会直接报 refused 或卡住。nc -zv 10.0.0.12 3306也可以脚本里更好用。3.4 账号层的创建与授权MySQL 8.0 语法有变化确认网络和监听都没问题接下来就是账号。MySQL 8.0 在这块改了一个很关键的行为不再支持用 GRANT 语句隐式创建用户。5.7 时代你可以直接GRANT ALL ON *.* TO root% IDENTIFIED BY xxx;一条语句把用户建出来再授权。8.0 里这么写会直接报语法错误。必须拆成两步CREATE USER root% IDENTIFIED BY YourStrongPass!2024; GRANT ALL PRIVILEGES ON *.* TO root% WITH GRANT OPTION; FLUSH PRIVILEGES;或者更推荐的做法建一个专用账号只给业务库的权限CREATE USER app192.168.1.% IDENTIFIED BY YourStrongPass!2024; GRANT SELECT, INSERT, UPDATE, DELETE ON business_db.* TO app192.168.1.%; FLUSH PRIVILEGES;FLUSH PRIVILEGES在 8.0 里其实不是必须的因为CREATE USER和GRANT会实时更新内存中的权限表。但加上无妨主要是习惯问题也能避免某些客户端工具缓存导致的错觉。授权范围上*.*是所有库所有表business_db.*是某个库的所有表business_db.orders是具体某张表。给业务账号的时候按最小权限来别动不动就ALL ON *.*。4. 认证插件MySQL 8.0 远程连接被拒最常见的隐形杀手4.1 caching_sha2_password 与老客户端的兼容问题MySQL 8.0 把默认认证插件从mysql_native_password换成了caching_sha2_password。这个改动本身是好事安全性更高但它带来一个非常典型的报错Authentication plugin caching_sha2_password cannot be loaded: The specified module could not be found.这个报错通常出现在用老版本客户端、老版本驱动、老版本图形化工具连接 8.0 的时候。服务端明明授权了网络也通密码也对就是卡在认证握手阶段。原因是客户端不认这个新插件的加密流程。解决办法有两条路。一条是升级客户端或驱动JDBC 用 8.0 以上的mysql-connector-javaPython 用pymysql的新版本或者mysql-connector-python命令行工具用 8.0 自带的mysql。另一条是把账号的认证插件改回老的ALTER USER app192.168.1.% IDENTIFIED WITH mysql_native_password BY YourStrongPass!2024;改完立刻生效不用重启。但我个人建议优先升级客户端mysql_native_password是为了兼容历史系统保留的新项目没必要往回退。只有在确实动不了客户端版本的时候才用这招。4.2 检查账号当前用的是哪个插件一条 SQL 就能看清SELECT user, host, plugin FROM mysql.user WHERE user app;如果plugin列显示caching_sha2_password而你的客户端不支持那就对上了。注意ALTER USER改插件的时候必须带上密码不能只写插件名否则会报错或者把密码置空。还有一种情况是plugin显示auth_socket。这是 Ubuntu 系某些安装方式下的默认行为root 账号用它认证走的是操作系统用户身份根本不校验密码。这种账号从远程连是绝对连不上的因为它压根不接受 TCP 密码认证。看到auth_socket就要改成密码认证ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY NewPass!2024;4.3 密码策略组件可能拦住你的密码MySQL 8.0 默认装了validate_password组件会强制密码复杂度。有时候你CREATE USER报这个错ERROR 1819 (HY000): Your password does not satisfy the current policy requirements不是语法问题是密码太弱。查看当前策略SHOW VARIABLES LIKE validate_password%;关键的两个是validate_password.policy和validate_password.length。policy有 LOW、MEDIUM、STRONG 三档MEDIUM 要求密码含数字、大小写字母和特殊字符。开发环境想省事可以调低SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;生产环境不建议动这个密码强度该有还是得有。本地 Docker 环境图快可以临时降一下重启后失效也不会影响别的实例。5. Docker 环境下的 MySQL 8.0 远程连接特别处理5.1 官方镜像的默认行为和端口映射现在很多人用 Docker 装 MySQL 8.0图的就是干净。但 Docker 场景下远程连接问题有它自己的特点。启动命令一般是docker run -d --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourStrongPass!2024 \ -e MYSQL_ROOT_HOST% \ mysql:8.0这里MYSQL_ROOT_HOST%是关键。官方镜像在初始化的时候会根据这个环境变量决定要不要额外创建一条root%记录。不写这个变量默认只建rootlocalhost容器内部能连宿主机通过映射端口也能连上是因为……等等这里要澄清一个常见误解宿主机用127.0.0.1:3306连容器来源地址在 MySQL 看来是网关地址比如 172.17.0.1不是 localhost。所以如果只有rootlocalhost宿主机连接同样会被拒。这就是为什么很多人 Docker 起完 MySQL本地工具连不上非要加MYSQL_ROOT_HOST才行。注意MYSQL_ROOT_HOST只在数据目录为空、初始化首次运行时生效。如果你已经起过一次容器、数据卷里已经有数据了再改这个环境变量重启是不会新建账号的。这种情况得手动进容器建docker exec -it mysql8 mysql -uroot -p然后在里面执行CREATE USER那套语句。5.2 容器内 bind-address 与宿主机防火墙的关系官方 MySQL 8.0 镜像里的默认配置bind-address是*也就是监听所有接口所以容器这一层通常不用改。真正拦人的往往是宿主机防火墙对映射端口的限制以及云主机的安全组。还有一个容易忽略的点-p 3306:3306把端口暴露在宿主机所有网卡上。如果宿主机有公网 IP等于数据库直接挂公网了。更安全的做法是绑定具体地址-p 127.0.0.1:3306:3306或者只绑内网 IP让外部完全访问不到业务通过内网连。这一点在本地开发机上无所谓上了云服务器一定要留意。5.3 数据卷持久化带来的账号残留问题用-v挂载数据目录之后账号信息是跟着数据卷走的。你可能遇到这种情况之前用某个密码初始化过后来忘了重新docker run又用了新密码结果怎么都连不上。因为数据卷没删MySQL 沿用旧的账号数据环境变量里的新密码被忽略了。排查这个问题的办法是进容器看账号表或者干脆停掉容器删掉数据卷重新初始化docker rm -f mysql8 docker volume rm mysql8_data重新跑一遍启动命令。当然这是开发环境才敢这么干生产环境的数据卷删了就是事故。6. 常见报错速查与实操避坑清单6.1 报错信息对照表把前面几层的问题汇总成一张表遇到报错直接对号入座报错信息大概率原因处理方向Cant connect to MySQL server (111)端口未监听或被防火墙拒绝查bind-address、防火墙、安全组Connection timed out网络不可达或安全组静默丢包查路由、安全组规则Access denied for user rootx.x.x.x缺少对应 host 的账号记录建root%或网段账号Access denied ... (using password: YES)密码错误或账号不匹配重置密码确认 host 匹配Authentication plugin cannot be loaded客户端不支持 caching_sha2升级客户端或改认证插件Host x.x.x.x is not allowed to connect完全没有匹配的账号创建账号并授权Too many connections连接数打满查max_connections排查连接泄漏Your password does not satisfy...密码策略拦截调整validate_password或换强密码这张表我自己排查问题时一直用基本能覆盖九成以上的场景。剩下的疑难杂症多半是多个因素叠加比如安全组没开加上账号没建那就得一层层剥。6.2 我实际踩过的几个坑第一个坑是改了 host 忘了刷新权限认知。早期我用UPDATE mysql.user SET host% WHERE userroot直接改表改完发现原来的rootlocalhost没了变成只有root%结果本地 socket 连接行为变得很奇怪。正确做法永远是CREATE USER新建一条而不是改现有记录。直接改系统表在 8.0 里容易出问题因为权限表是 InnoDB 的字段结构和内部缓存有联动官方也不推荐手工 UPDATE。第二个坑是 skip-name-resolve 带来的主机名匹配失效。有些优化配置里会加skip-name-resolve目的是跳过 DNS 反查加快连接速度。开了之后账号表里 host 部分写域名或者主机名的记录就全部失效了只能用 IP 或者%。我遇到过有人账号建的是appdbserver.example.com服务端开了skip-name-resolve结果怎么都匹配不上。排查时执行SHOW VARIABLES LIKE skip_name_resolve;确认一下。第三个坑是密码里的特殊字符在命令行被 shell 吃掉。密码里有$、!、\这些字符时直接写在命令行里容易被 shell 展开。比如mysql -uroot -pAbc$123里的$123会被当成变量。规避办法是用单引号包起来或者干脆用交互式输入密码不加-p后面的值。这个问题跟 MySQL 本身无关但排查起来特别费时间因为报错是Access denied看起来像密码错了实际上是你敲进去的密码跟你想的不一样。第四个坑是 Docker 容器的时区和字符集。这个严格说不算连接问题但我见过有人因为容器默认字符集是 latin1连上去之后中文乱码误以为是连接参数的问题。启动时加--character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci或者用配置文件挂载进去能省不少事。6.3 安全收尾远程账号别用 root排查完能连上了最后一件事是收尾。远程连接用 root 是最不推荐的做法一旦密码泄漏整个实例所有库都没了。合理的方式是保留rootlocalhost只用于本地维护为业务建专用账号host 限定到具体网段权限按库、按表最小化授权定期用SHOW GRANTS FOR userhost;审计权限。审计权限这条命令值得记住输出是一串 GRANT 语句一眼就能看出这个账号到底能干什么。有时候交接过来的老系统账号权限大得吓人ALL PRIVILEGES ON *.* WITH GRANT OPTION这种就得赶紧收。提示改完账号和权限后建议从一台真实的远程机器上完整走一遍连接测试别只在数据库本机用mysql -h 127.0.0.1验证。本机连 127.0.0.1 走的还是 localhost 匹配验证不出远程账号的问题。7. 一套可以直接抄的完整配置流程如果你是全新环境想要一个从零到远程可连的标准动作按下面这个顺序走基本不会返工。第一步确认服务在跑并且监听地址正确systemctl status mysqld ss -lntp | grep 3306监听地址是127.0.0.1的话去配置文件改成0.0.0.0并重启再用SHOW VARIABLES LIKE bind_address;确认。第二步放行防火墙和云安全组只开业务来源 IP。第三步进数据库看账号表SELECT user, host, plugin FROM mysql.user;第四步按需建账号业务账号优先CREATE USER app192.168.1.% IDENTIFIED BY StrongPass!2024; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, ALTER ON business_db.* TO app192.168.1.%; FLUSH PRIVILEGES;如果客户端老补一句改插件ALTER USER app192.168.1.% IDENTIFIED WITH mysql_native_password BY StrongPass!2024;第五步从远程机器实测用mysql -h 内网IP -uapp -p连或者写个小脚本跑一条查询。telnet只能验证端口通验证不了认证别用它当最终结论。第六步把连接参数写进应用的连接池配置注意加上useSSL、serverTimezone、characterEncoding这些常见参数避免时区和编码问题反过来又怀疑到连接上。整套流程走下来大部分 MySQL 8.0 远程连接问题都能定位并解决。真正难的不是某一条命令而是知道当前卡在哪一层。我的经验是遇到连不上先别改配置先把报错原文看清楚再用ss、telnet、账号表这三样东西分别验证网络层、端口层和账号层一层层往上推比上来就改bind-address靠谱得多。踩过几次坑之后你会发现rootlocalhost和root%这个小细节才是卡住最多人的那道坎。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询