MySQL报1130错误的根本原因是权限匹配失败:虽有'root'@'%'记录,但因匹配优先级更高(如'root'@'localhost')或CURRENT_USER()返回的host不匹配(如'host.docker.internal'),或bind-address=127.0.0.1限制监听、防火墙/安全组拦截导致连接无法到达权限校验环节。

SELECT user, host FROM mysql.user 显示 host='%' 但还是报 1130
这说明 MySQL 确实有 % 记录,但权限系统根本没匹配到它——因为匹配顺序优先级更高的一条记录抢先“截胡”了。比如你看到结果里同时存在:
'root'@'localhost''root'@'127.0.0.1''root'@'%'
而你用 mysql -h localhost -u root -p 连,MySQL 就会选 'root'@'localhost',哪怕 'root'@'%' 存在也完全不生效。
真正决定走哪条记录的,是客户端实际声明的 -h 值(或连接字符串里的 host),以及 MySQL 内部解析出的 hostname(比如 Docker 容器里连宿主 MySQL,host.docker.internal 可能被 DNS 解析成真实 IP,也可能直接传过去)。
执行 SELECT USER(), CURRENT_USER();,看返回的第二列——那个才是 MySQL 实际用来查权限的 user@host。如果它是 'root'@'host.docker.internal',但表里没这条记录,'root'@'%' 也救不了你。
GRANT 后 FLUSH PRIVILEGES 不生效的常见原因
很多人执行完 GRANT ALL ON *.* TO 'root'@'%'; 就以为完了,但连接仍失败。问题往往出在:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 没加
IDENTIFIED BY 'xxx':MySQL 8.0+ 默认用caching_sha2_password插件,旧客户端(如某些 Navicat 版本、Python 的 pymysql 低版本)不认,得显式指定认证方式:ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'your_password'; -
FLUSH PRIVILEGES;没执行,或执行了但没成功(比如权限不足、语法错误);可执行SHOW WARNINGS;看有没有隐式报错 - 客户端缓存了旧连接参数(尤其是 JDBC URL 或连接池),改完服务端后没重启应用或清空连接池
bind-address=127.0.0.1 导致 % 权限形同虚设
就算 mysql.user 里 host 全是 %,只要 my.cnf 里还留着 bind-address = 127.0.0.1,MySQL 就只监听本地回环口,外部 TCP 包根本进不来。
检查命令:ss -tlnp | grep :3306。如果输出只有 127.0.0.1:3306,那所有远程连接请求都在网络层就被丢弃了,压根不会走到权限校验那步。
正确做法是:
- 把配置改成
bind-address = 0.0.0.0(IPv4)或注释掉该行(MySQL 8.0.13+ 默认监听所有地址) - 改完必须重启 MySQL:
systemctl restart mysqld(Linux)或服务管理器(Windows) - 再运行
ss -tlnp | grep :3306,确认出现*:3306或0.0.0.0:3306
防火墙和云服务商安全组常被忽略
本地 ss 看到 *:3306,mysql.user 也有 'user'@'%',但外网还是连不上?大概率卡在中间层:
- Linux 防火墙(
firewalld或iptables)没放行 3306:firewall-cmd --query-port=3306/tcp查,没开就加:firewall-cmd --add-port=3306/tcp --permanent && firewall-cmd --reload - 云服务器(阿里云/腾讯云/AWS)的安全组规则没配——这个比本地防火墙更常漏掉,必须手动在控制台添加入方向规则,协议 TCP,端口 3306,源 IP 根据需要填
0.0.0.0/0或具体段 - Docker 容器跑 MySQL 时,宿主机防火墙 + 容器端口映射(
-p 3306:3306)都得对得上
真正难缠的不是权限本身,而是你改了 A 却以为是 B 的问题——比如看到 host='%' 就停手,却不知道 CURRENT_USER() 返回的是 'root'@'host.docker.internal',也不知道 bind-address 还钉死在 127.0.0.1 上。每一步都要验证,不能靠“应该没问题”。

















