发布时间:2026-09-21 12:30 更新时间:2026-09-21 12:30 阅读量:0
本地连 MySQL 一切正常,换到另一台机器用客户端连过去就提示 Access denied 或者干脆 Connection refused,这是站长和运维最常遇到的一类问题。很多人第一反应是去改密码或者重装数据库,其实远程访问要依次通过三道关:账号授权里的 user@host 是否匹配、服务端是否监听在可被外部访问的地址、系统防火墙是否放行了 3306 端口。任何一层没打通,表现都是"连不上",但原因完全不同。
这篇文章按这三层顺序拆开讲,每层给出可执行的检查命令,新手可以照着一步步排查,老手也能借这套顺序避免来回试错。文中涉及的具体语法和默认值,以你所装 MySQL 版本的官方文档为准。
MySQL 的账号不是只有一个用户名,而是由 user 和 host 两部分共同组成,写成 '用户名'@'主机来源'。登录时 MySQL 会同时拿客户端 IP 和账号里的 host 做比对,只有两边都对上才算这个账号。常见的写法有几种:'app'@'localhost' 只允许本机 socket 或 127.0.0.1 登录;'app'@'127.0.0.1' 只匹配回环地址;'app'@'192.168.1.%' 匹配同网段;'app'@'%' 表示任意来源。
这里有个容易踩的坑:MySQL 在多个候选账号中会挑选"最具体"的那个匹配。假如你同时存在 'app'@'localhost' 和 'app'@'%',从本机登录时命中的是 localhost 那条,权限和密码都按它来,而不是按 % 那条。所以改密码时如果只改了 % 的账号,本机登录仍然用旧密码,就会让人以为"密码改了没生效"。排查时先看当前到底命中哪条:
mysql -u root -p -e "SELECT user, host, plugin FROM mysql.user WHERE user='app';"
mysql -u root -p -e "SHOW GRANTS FOR 'app'@'%';"
确认要开放远程访问后,授权语句要同时写清来源和权限范围,不要图省事直接给 ALL 加 %。更稳妥的做法是先建一个只允许业务网段的账号:
CREATE USER 'app'@'192.168.1.%' IDENTIFIED BY '换成高强度密码';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app'@'192.168.1.%';
FLUSH PRIVILEGES;
需要提示的是,MySQL 8 默认使用 caching_sha2_password 认证插件,部分较老的客户端驱动可能不兼容,这时客户端会报认证插件相关的错误。是否要改成 mysql_native_password 要结合你的客户端版本决定,不建议无脑降级,具体以官方文档和实际客户端为准。另外授权完成后,如果还是 Access denied,可以看下错误信息里给出的 "using password: YES/NO",它能区分是密码错还是压根没带密码。
账号对了仍然连不上,很可能是 MySQL 只监听了回环地址。早期版本默认 bind-address = 127.0.0.1,只接受本机连接,外部请求连 TCP 握手都建立不了,客户端通常表现为 Connection refused 而不是 Access denied。判断方法很直接,看端口监听在哪个地址:
ss -lntp | grep 3306
如果输出是 127.0.0.1:3306,说明只监听本机;如果是 0.0.0.0:3306 或 :::3306,说明已经在所有网卡上监听。要让外部能连,需要在配置文件里调整。Debian/Ubuntu 系一般在 /etc/mysql/mysql.conf.d/mysqld.cnf,CentOS/RHEL 系通常在 /etc/my.cnf:
[mysqld]
bind-address = 0.0.0.0
也可以只绑定内网网卡地址,例如 bind-address = 192.168.1.10
部分环境还需确认 skip-networking 未被启用
改完要重启服务才生效,用 systemctl restart mysql 或 systemctl restart mysqld,具体服务名以你的系统为准。这里提醒一句安全边界:把 3306 暴露到公网风险很高,比较稳妥的方案是只绑定内网地址,外部通过 SSH 隧道或跳板机访问,而不是直接对全网开放。
监听和授权都对了,连接仍然超时,就要看防火墙。Linux 上常见的有 firewalld、ufw 和 iptables 三套,云服务器还要额外检查厂商的安全组规则,安全组在云平台侧,服务器本地命令看不到。以 firewalld 为例,放行 3306 可以限定来源网段:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" accept'
firewall-cmd --reload
firewall-cmd --list-all
用 ufw 的系统则是 ufw allow from 192.168.1.0/24 to any port 3306。放行后建议从客户端实测端口连通性,而不是只靠猜。可以用 nc 或 telnet 探测:能立刻返回成功说明链路通,卡住不动多半是被防火墙或安全组丢包,返回 refused 则说明端口没监听或服务没起来。三种表现对应的问题层次不同,先分清再动手,能省下大量时间。
把三层串起来看,排查顺序建议是:先用 ss 看监听地址,再从客户端测端口连通性,最后看账号授权和命中情况。这样从底层往上走,每一步都能缩小范围,避免在错误的方向上反复改配置。远程访问配置完成后,别忘了给数据库账号设置足够强的密码、限制可访问网段,并把 3306 的开放范围控制在业务真正需要的范围内。
| 📑 | 📅 |
|---|---|
| Nginx与PHP上传目录权限:www-data、umask与0777的坑 | 2026-09-21 |
| systemd-resolved 与 /etc/resolv.conf 冲突排查 | 2026-09-21 |
| MySQL索引失效排查:EXPLAIN执行计划与隐式转换定位 | 2026-09-21 |
| Docker 容器健康检查实战:HEALTHCHECK 与 unhealthy 自动重启 | 2026-09-21 |
| Nginx location 匹配优先级实战:=、^~、~ 命中顺序验证 | 2026-09-21 |
| Docker容器时间漂移与crond定时任务错乱:TZ与宿主时间源协同 | 2026-09-21 |
| systemd timer 替代 crontab 实战:OnCalendar、随机延迟与失败重试 | 2026-09-22 |
| Nginx 代理 WebSocket 频繁断连:Upgrade 头、超时与保活配置 | 2026-09-22 |
| 服务器被DDoS打满带宽:自查连接分布与限速缓解 | 2026-09-22 |
| Linux磁盘IO高却查不出:iotop与blkio限速实战 | 2026-09-22 |