MySQL中localhost与127.0.0.1是两个完全独立的host,'user'@'localhost'和'user'@'127.0.0.1'对应不同权限记录:前者走Unix socket(Linux/macOS)或TCP(Windows),后者强制走IPv4 TCP/IP,连接方式不同导致权限匹配路径分离,不可互替。

localhost 和 127.0.0.1 在 MySQL 中根本不是同一个 host
MySQL 的权限系统严格按 'user'@'host' 二元组匹配,'root'@'localhost' 和 'root'@'127.0.0.1' 是两条完全独立的用户记录,哪怕密码相同、权限相同,也不能互相替代。这不是配置疏漏,是设计使然——MySQL 对 localhost 有硬编码特殊处理:只要 host 字段字面量等于 localhost,客户端就默认走 Unix socket(Linux/macOS)或退化为 TCP(Windows),而 127.0.0.1 始终强制走 IPv4 TCP/IP 协议栈。
连接方式不同导致权限匹配路径完全不同
你执行 mysql -u root -p(不带 -h)时,客户端默认用 localhost,触发 Unix socket 连接,MySQL 查找 'root'@'localhost';但加了 mysql -h 127.0.0.1 -u root -p,就变成 TCP 连接,MySQL 只查 'root'@'127.0.0.1' 或更宽泛的 'root'@'%'。这两条路径在权限校验阶段就分开了,中间不互通。
- 验证当前实际匹配的用户:
SELECT USER(), CURRENT_USER();—— 前者是客户端声明的,后者才是 MySQL 真正用来查权限的 - 看连接底层协议:
\s或STATUS,关注Connection:行:出现Localhost via UNIX socket表示走 socket,127.0.0.1 via TCP/IP表示走 TCP - Windows 用户注意:
localhost在 Windows 上几乎总是被解析成127.0.0.1,所以本地建'u'@'localhost'很可能根本用不上
改 host 字段不是改权限,而是直接废掉账号
别用 UPDATE mysql.user SET host = 'localhost' WHERE user = 'u' 这种操作。MySQL 8.0+ 明确禁止更新 host 字段,执行后该账号会永久失效:表里还能查到,但任何连接都报 Access denied。这是不可逆的损坏,不是缓存没刷。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 正确做法是先
DROP USER 'u'@'old_host';,再CREATE USER 'u'@'localhost' IDENTIFIED BY 'pwd'; -
GRANT语句中的host必须和CREATE USER完全一致,写错一个字符(比如多空格、大小写)都会导致权限不生效 -
FLUSH PRIVILEGES;在用GRANT/CREATE USER操作后非必需(MySQL 8.0.16+ 自动重载),但手动执行也无害;若用了UPDATE,它也无法修复已损坏的账号
caching_sha2_password 插件会让问题表现得更像权限错误
MySQL 8.0+ 默认用 caching_sha2_password 认证插件,但很多老客户端(Python mysql-connector-python mysql_native_password。握手失败时,MySQL 统一返回 Access denied for user,看起来像权限不对,其实是插件不兼容。
- 查当前用户插件:
SELECT user, host, plugin FROM mysql.user WHERE user = 'u'; - 切回兼容插件:
ALTER USER 'u'@'localhost' IDENTIFIED WITH mysql_native_password BY 'pwd'; - 改完必须执行
FLUSH PRIVILEGES;(这次真需要,因为插件变更不会自动重载)
实际权限不一致的问题,核心不在“为什么不一样”,而在于你是否意识到 MySQL 把它们当两个账户管理。最稳妥的做法是:明确知道脚本或工具连的是哪个 host,就只为那个 host 创建用户并授权,不依赖模糊匹配。否则,一个 localhost 配置,加上 GUI 工具自动转写为 127.0.0.1,就能让整个本地调试流程卡住。










