mysql中%不匹配localhost,因为认证时按host精确度排序,localhost作为字面量主机名优先级高于通配符%,只要存在'user'@'localhost'账户,本地连接就始终匹配它而非'user'@'%';默认mysql -u user -p走unix socket,等效于-h localhost,实际认证用户为'user'@'localhost'。

为什么%不匹配localhost连接?
因为MySQL认证时按主机名精确度排序,localhost是字面量主机名,比通配符%更具体,所以只要存在'user'@'localhost'这一行,本地连接就永远优先匹配它,根本不会轮到'user'@'%'。
这不是“%漏掉了localhost”,而是匹配逻辑本身就不走那条路——就像你家门牌号是“朝阳路123号”,系统不会因为你办了“全国通用通行证(%)”就跳过门牌直接进门。
mysql -u user -p连的是localhost还是127.0.0.1?
默认走 Unix socket,等效于指定 -h localhost,最终认证用户是 'user'@'localhost';而 -h 127.0.0.1 才走 TCP,匹配 'user'@'127.0.0.1' 或 'user'@'%'(如果没更具体的)。
-
mysql -u test→test@localhost(socket,查host='localhost') -
mysql -h 127.0.0.1 -u test→test@127.0.0.1(TCP,可能匹配%) -
mysql -h controller -u test→ 取决于 DNS 解析结果,若解析为127.0.0.1,仍算127.0.0.1,不是localhost
MySQL 5.7+ 和 8.0 真的“包含”localhost吗?
不是“包含”,是行为变化:5.7+ 默认启用 skip-name-resolve 且对 localhost 连接做了特殊路由优化,使得没有显式 'user'@'localhost' 账户时,'user'@'%' 可能被选中——但这依赖于 user 表中无冲突条目,且不等于语义上%覆盖localhost。
实操中仍建议:只要需要本地访问,就明确创建 'user'@'localhost';只要需要远程访问,就单独给 'user'@'%' 或更细粒度 IP。别赌版本行为。
- 查看当前匹配顺序:
SELECT user, host FROM mysql.user ORDER BY host DESC;(越靠前越优先) - 检查实际登录用的 host:
SELECT USER(), CURRENT_USER();——前者是客户端声明的,后者才是 MySQL 实际匹配的账户 - 删除冗余匿名用户:
DROP USER ''@'localhost';,否则它们会劫持空用户名的连接
授权后还是报Access denied for user 'x'@'localhost'怎么办?
错误信息里的 @'localhost' 就是线索:说明 MySQL 已经决定用这个 host 匹配,但该账户不存在或密码错。此时'x'@'%'再全权限也没用。
- 先确认账户是否存在:
SELECT user, host FROM mysql.user WHERE user = 'x'; - 如果只有
x@'%',立刻补上:GRANT ALL ON *.* TO 'x'@'localhost' IDENTIFIED BY 'pwd'; - 密码不一致?MySQL 8.0+ 不同 host 账户可设不同密码,
ALTER USER 'x'@'localhost' IDENTIFIED BY 'same_pwd'; - 改完必须执行:
FLUSH PRIVILEGES;(仅在直接改表后需要,GRANT 自动刷新)
最易被忽略的一点:应用配置里写的是 controller,但 /etc/hosts 把它映射到了 127.0.0.1,这时连出来的是 'x'@'127.0.0.1',不是 localhost——得额外授权 'x'@'127.0.0.1' 或删掉 hosts 映射。











