结论:这不是权限配置漏了,而是mysql拒绝了来自该ip的连接请求——原因通常是host_cache中该ip已被临时封禁,或用户账户的host字段不匹配实际来源ip;需查performance_schema.host_cache确认阻塞状态,执行flush hosts或reset hosts清空缓存,并比对select user(), current_user()结果验证host匹配性。

直接说结论:这不是权限配置漏了,而是 MySQL 拒绝了来自该 IP 的连接请求 —— 原因通常是 host_cache 中该 IP 已被临时封禁,或用户账户的 host 字段不匹配实际来源 IP。
查 host_cache 表确认是否被自动屏蔽
MySQL 会把连续失败的连接尝试记入内存中的 host_cache,达到 max_connect_errors(默认 100)后直接拒绝该 IP 的所有新连接,哪怕密码正确。
- 先检查当前被标记为阻塞的 IP:
SELECT IP, HOST, SUM_CONNECT_ERRORS, HOST_VALIDATED FROM performance_schema.host_cache WHERE SUM_CONNECT_ERRORS >= @@global.max_connect_errors;
- 如果看到你的客户端 IP 出现在结果里,说明它已被缓存拦截 —— 这时改用户权限、重启服务都无效
- 临时清空缓存(立即生效,无需重启):
FLUSH HOSTS;
或者更精准地只重置特定 IP:RESET HOSTS;
(注意:MySQL 8.0.26+ 才支持RESET HOSTS;旧版本只能用FLUSH HOSTS) - 想长期避免,可调大阈值:
SET GLOBAL max_connect_errors = 1000;
(但需配合防火墙策略,不能无限制放宽)
验证用户 host 字段是否匹配真实来源 IP
执行 SELECT USER(), CURRENT_USER();,对比两个结果:
-
USER()显示「你声称自己是谁」—— 即连接时用的用户名 + 客户端 IP(可能被 NAT/容器/namespace 伪装) -
CURRENT_USER()显示「MySQL 认为你是谁」—— 即权限表中匹配到的user@host条目 - 常见错配场景:
– 容器内连宿主机 MySQL,USER()返回root@172.17.0.1,但权限表只有root@localhost或root@127.0.0.1
– 使用--protocol=TCP连本地 MySQL,却误以为走的是 socket,结果来源 IP 是127.0.0.1而非localhost - 修复方式不是盲目加
%,而是按实际来源建用户:CREATE USER 'app'@'172.17.0.1' IDENTIFIED BY 'pwd'; GRANT ALL ON db.* TO 'app'@'172.17.0.1';
检查 bind_address 和 skip_networking 是否关死了远程入口
即使用户权限全开,MySQL 本身也可能根本没监听 TCP 连接。
- 运行
mysql -e "SHOW VARIABLES LIKE 'bind_address'; SHOW VARIABLES LIKE 'skip_networking';"
,确认:
–bind_address不是127.0.0.1(除非你确定只接受本机 loopback)
–skip_networking必须是OFF - 如果修改了配置,记得重启 mysqld;但注意:Docker 容器中改
my.cnf后必须重建容器,热加载不生效 - Linux 上用
ss -tlnp | grep :3306
看 MySQL 是否真在监听目标 IP:port;若只显示127.0.0.1:3306,外部 IP 就连不上
真正卡住人的地方,往往不是权限语句写错了,而是 CURRENT_USER() 和你预想的 host 完全对不上 —— 特别是在容器、K8s、网络命名空间环境下,IP 经过多次转换,host_cache 又默默记下了某次失败的解析结果。先查表、再刷缓存、最后比对两个 USER(),三步下来,90% 的 “Host is not allowed” 就定位清楚了。











