mysql中'foo'@'localhost'与'foo'@'%'是完全独立账号,localhost强制走unix socket仅匹配host='localhost'记录,而-h127.0.0.1走tcp/ip只匹配'foo'@'127.0.0.1'或'foo'@'%';%不覆盖localhost,且需确保bind-address=0.0.0.0、flush privileges生效、防火墙放行3306。

MySQL 给 localhost 授权后 IP 无法访问,不是授权没生效,而是根本没匹配到那条记录——'user'@'localhost' 和 'user'@'192.168.1.100'(或 'user'@'%')在 MySQL 里是完全不同的账号,权限、密码、认证插件互不继承。
为什么 mysql -ufoo -h127.0.0.1 -p 连不上,但 mysql -ufoo -hlocalhost -p 可以?
这是因为 MySQL 对 localhost 做了特殊处理:它强制走 Unix socket 连接,只查 host = 'localhost' 的记录;而 -h127.0.0.1 明确走 TCP/IP,此时匹配的是 host = '127.0.0.1' 或 host = '%' 的行,跟 localhost 记录毫无关系。
- 执行
SELECT user, host FROM mysql.user WHERE user = 'foo';,如果只有foo@localhost,那所有带-h参数的连接都会被拒绝 -
localhost不等价于127.0.0.1,也不被%匹配(%只覆盖 TCP/IP 类型连接) - IPv6 启用后,
localhost可能解析为::1,这时还得额外加'foo'@'::1'才能覆盖
GRANT ... TO 'user'@'%' IDENTIFIED BY 'pwd' 之后还是连不上 IP?
常见原因不是语法错,而是配置没生效或服务没监听。先确认三件事:
- 是否改了
my.cnf里的bind-address?默认是127.0.0.1,必须改成0.0.0.0或删掉该行,否则操作系统直接丢包,报Connection refused - 是否执行了
FLUSH PRIVILEGES;?GRANT 语句本身会刷新,但如果你用UPDATE mysql.user改表,就必须手动刷 - 防火墙/安全组是否放行 TCP 3306 入站?Windows 需
netsh加规则,云服务器得配安全组
为什么 UPDATE mysql.user SET host = '%' 看似成功却无效?
MySQL 8.0+ 对系统表有强校验,直接 UPDATE mysql.user 很可能静默失败,或者导致后续认证异常(比如 plugin 字段被重置)。更稳妥的方式是显式创建新账号:
- 用
CREATE USER 'foo'@'%' IDENTIFIED BY 'pwd';+GRANT ... TO 'foo'@'%'; - 若要复用 root,用
ALTER USER 'root'@'localhost' RENAME TO 'root'@'%';(MySQL 5.7+) - 别用
'root'@'%'上生产,应限定网段,如'app'@'10.0.2.%'
真正容易被忽略的点是:MySQL 的权限匹配发生在连接建立前,而连接能否建立又取决于 bind-address 和防火墙。这两层过滤缺一不可,且顺序不能颠倒——先让服务听得到,再让账号认得着。











