‘user’@‘localhost’和‘user’@‘127.0.0.1’是两个完全独立的账号,前者强制走unix socket,后者强制走tcp/ip回环,权限、连接方式及安全控制均互不影响。

MySQL里 'user'@'localhost' 和 'user'@'127.0.0.1' 是两个完全独立的账号
MySQL 的权限系统靠 User + Host 两字段联合唯一识别一个账户。哪怕用户名一样,'app'@'localhost' 和 'app'@'127.0.0.1' 在 mysql.user 表里就是两条不同记录,授权、密码、甚至是否启用都互不影响。
常见错误现象:
- 执行
CREATE USER 'app'@'localhost' IDENTIFIED BY 'pwd'后,mysql -u app -p能连,但mysql -h 127.0.0.1 -u app -p报Access denied - DBeaver 填
localhost连不上,换填127.0.0.1却能连——说明只建了@'127.0.0.1',没建@'localhost'
localhost 强制走 Unix socket,127.0.0.1 强制走 TCP/IP 回环
这是行为差异的根源。MySQL 客户端遇到 -h localhost(或不加 -h)时,会跳过网络栈,直接连接 /var/run/mysqld/mysqld.sock(Linux)或 /tmp/mysql.sock(macOS)。而 -h 127.0.0.1 一定走 IPv4 TCP 协议栈,哪怕只是本机回环。
这意味着:
-
localhost不受防火墙、bind-address、SELinux 网络策略影响 -
127.0.0.1可能被iptables或ufw拦截,也可能因bind-address = 127.0.0.1配置缺失而失败 - Windows 上多数 MySQL 客户端(如老版 Navicat)会把
localhost自动解析成127.0.0.1,导致走 TCP 而非 socket,此时即使建了@'localhost'也连不上
验证当前连接实际走的是哪条路径
登录成功后,在 MySQL 客户端里运行 \s 或 STATUS,重点看 Connection: 这一行:
-
Connection: Localhost via UNIX socket→ 真正走了 socket,用的是'user'@'localhost'权限 -
Connection: 127.0.0.1 via TCP/IP→ 走的是 TCP,匹配的是'user'@'127.0.0.1'或'user'@'%'
注意:命令行测试必须严格区分参数。不加 -h 默认是 localhost;加了 -h 127.0.0.1 就绕过了 socket 逻辑,哪怕你本地只建了 @'localhost' 用户也会失败。
建用户时最容易踩的坑
想锁死本地访问,就别想着“先建 @'%' 再改 Host 字段”。MySQL 8.0+ 明确禁止 UPDATE mysql.user 的 Host 列,强行更新会导致该账号永久失效(SELECT 还能查到,但任何连接都报 Access denied)。
正确做法只有三步:
-
DROP USER 'u'@'%'(或旧 host 对应的记录) CREATE USER 'u'@'localhost' IDENTIFIED BY 'pwd'-
GRANT SELECT ON db.* TO 'u'@'localhost'(Host必须和 CREATE 完全一致)
最后执行 FLUSH PRIVILEGES(MySQL 8.0.16+ 非必需,但无害)。如果还要兼容某些强制解析 localhost 为 127.0.0.1 的 GUI 工具,就得额外建一个 'u'@'127.0.0.1' ——但这就不是“真正锁死本地”了。











