查mysql.user表时需用order by length(host) desc, host desc近似还原匹配优先级,因mysql按host具体性排序('localhost'优于'%'),空用户''@'localhost'会干扰本地连接,current_user()与user()不一致说明实际匹配账号与预期不符。

查mysql.user表时怎么看出哪个Host实际生效
直接SELECT User, Host FROM mysql.user只能看到有哪些账号,看不出匹配顺序。MySQL内部按“越具体越靠前”排序,不是字典序。用ORDER BY LENGTH(host) DESC, host DESC能近似还原真实优先级——因为'localhost'长度是9,'192.168.1.%'是11,但前者更精确,所以MySQL实际把它排在前面;而'%'长度最短(1),永远垫底。
常见干扰项:''@'localhost'(空用户名+localhost)比'app'@'localhost'还靠后,但它存在时会拦截所有未指定-h的本地连接;'app'@'localhost'又会压制'app'@'%',哪怕后者密码正确。
CURRENT_USER()和USER()不一致说明什么
执行SELECT USER(), CURRENT_USER();,如果结果不同,说明MySQL没匹配到你“以为”的那条记录。比如USER()返回'app'@'192.168.5.23',但CURRENT_USER()是'app'@'localhost',这代表你用了mysql -uapp -p没加-h,MySQL强制走socket协议,只认localhost,哪怕你从远程IP发起连接也一样。
另一个典型场景:USER()是'app'@'DESKTOP-ABC123',CURRENT_USER()却是'app'@'%',说明MySQL找不到主机名精确匹配,降级用了通配符——这时得检查DNS反向解析是否开启,或干脆把host改成IP段。
为什么删了'foo'@'localhost'反而连不上
这不是bug,是MySQL权限匹配逻辑的必然结果。删掉'foo'@'localhost'后,如果只剩'foo'@'%',本地用mysql -ufoo -p仍可能失败,因为:
- MySQL启动时默认允许socket连接,它只查
Host = 'localhost'的记录,'%'不参与socket匹配 - 你可能忘了还有
''@'localhost'(匿名用户)存在,它会先于'foo'@'%'被选中 - MySQL要求至少一个具备SUPER权限的用户能通过
localhost登录,删光所有localhost账号可能导致无法恢复
安全做法是:先用mysql -uroot -p -h127.0.0.1连上,再删;或者保留'foo'@'localhost',用UPDATE mysql.user SET authentication_string = (SELECT authentication_string FROM mysql.user WHERE user='foo' AND host='%') WHERE user='foo' AND host='localhost';同步密码散列值。
GRANT之后权限还是不生效?先看CURRENT_USER()再动手
很多人一遇到Access denied就急着GRANT、FLUSH PRIVILEGES,但问题往往出在第一步:你根本没连到预期账号。执行GRANT SELECT ON db1.* TO 'app'@'%';后,如果当前连接是CURRENT_USER()显示'app'@'localhost',那这条授权完全无效——因为mysql.db表里没存'app'@'localhost'对db1的权限记录。
正确排查路径是:
- 先确认
CURRENT_USER()返回值 - 再查对应
User/Host在mysql.db或mysql.tables_priv里的权限记录 - 最后才决定是否需要
GRANT或调整Host字段
记住:MySQL权限校验第一关永远是mysql.user,匹配不到正确的User+Host组合,后面所有授权都白搭。











