mysql的access denied错误本质是认证三元组(user, host, plugin)不匹配:①同一用户名不同host(如localhost与127.0.0.1)被视为不同用户;②mysql 8.0+默认caching_sha2_password插件与旧客户端不兼容;③密码含特殊字符未正确转义或存在不可见空格;④host字段严格匹配,非范围授权逻辑。

这不是密码输错了那么简单——MySQL 的 Access denied for user 错误,本质是认证三元组不匹配。 它不是“你错”,而是“你连的不是那个账户”。下面直奔关键点。
为什么 mysql -u root -p 和 mysql -u root -p -h 127.0.0.1 可能一个通一个不通
MySQL 把 'root'@'localhost' 和 'root'@'127.0.0.1' 当作两个完全不同的用户。前者走 Unix socket(本地免网络),后者走 TCP/IP 协议栈。哪怕密码一样,授权记录也得分别存在。
- 执行
SELECT user, host, plugin FROM mysql.user WHERE user = 'root';,你会看到至少两行:一行host='localhost',一行host='127.0.0.1'(或'%') - 如果你只给
'root'@'localhost'设了密码,却用-h 127.0.0.1连,MySQL 就去查'root'@'127.0.0.1'—— 这条记录可能根本没设密码,或 plugin 不兼容 - IPv6 场景下还要注意
::1,它和127.0.0.1也不等价
caching_sha2_password 插件导致旧客户端连接失败
MySQL 8.0+ 默认用 caching_sha2_password 做认证插件,但很多老工具(如 MySQL 5.7 客户端、某些 Python 驱动旧版本、Navicat 旧版)只认 mysql_native_password。错误提示仍是 Access denied,但根源是握手失败,不是密码错。
- 查当前用户用的插件:
SELECT user, host, plugin FROM mysql.user WHERE user = 'your_user'; - 强制改回兼容插件:
ALTER USER 'your_user'@'%' IDENTIFIED WITH mysql_native_password BY 'your_pass'; - 改完必须执行
FLUSH PRIVILEGES;,否则不生效 - 注意:改插件不影响密码本身,只是换了一种加密交换方式
明明密码正确,using password: NO 却报错
这说明 MySQL 认为你没传密码,但它期望有密码 —— 往往是 shell 层面被吃掉了。
- 检查命令里有没有空格、tab 或不可见字符:
echo -n "mypass" | hexdump -C看末尾是不是0a(换行) - 特殊字符如
$、!、@在 bash 里会被展开,必须单引号包裹:mysql -u admin -p'P@ssw0rd!' - 如果密码含单引号,得用双引号并转义:
mysql -u admin -p"P@ssw0'rd!" - 配置文件(如
~/.my.cnf)里如果写了password = xxx,但值为空或注释了,也会触发using password: NO
授权后仍报 Access denied,FLUSH PRIVILEGES 真的有用吗
多数情况下没用 —— MySQL 8.0+ 的权限变更(CREATE USER、GRANT、ALTER USER)会自动刷新内存缓存,FLUSH PRIVILEGES 是个遗留惯性操作,仅在直接修改 mysql.user 表后才需要。
- 如果你用的是
GRANT ... TO 'u'@'h';,不用刷 - 如果你用
UPDATE mysql.user SET ... WHERE ...;,必须刷 - 但更推荐别直接 UPDATE 表:容易漏字段(比如
account_locked、password_last_changed),用ALTER USER更安全 - 真正卡住的往往是 host 匹配逻辑:MySQL 按
host字段最长匹配优先,'user'@'192.168.1.%'会比'user'@'%'先命中,但如果你只授了后者,前者没记录,就拒绝
最常被忽略的其实是 host 字段的语义精度——它不是“允许访问的范围”,而是“你这次连接声明自己来自哪里”。客户端填什么,服务端就严格按什么去查表。连对了协议、插件、密码,但 host 对不上,照样 Access denied。











