MySQL用户是'username'@'host'完整标识,CREATE USER仅注册账号不授任何权限,必须显式GRANT(如SELECT ON mydb.*)才可用;权限范围须精确指定数据库和host,否则易引发安全风险或访问失败。

直接说结论:MySQL 用户不是“用户名”一个字段,而是 'username'@'host' 这个完整标识;权限必须显式授予,CREATE USER 本身不带任何权限,不执行 GRANT 就连 SELECT 都不能用。
为什么新用户连 SELECT 都报错?
常见现象:执行 CREATE USER 'app'@'192.168.1.10' IDENTIFIED BY 'pwd123'; 后,用该用户登录却提示 ERROR 1045 (28000): Access denied for user 或 ERROR 1142 (42000): SELECT command denied。
原因很直接:CREATE USER 只是注册账号,不赋予任何权限。MySQL 默认给新用户零权限,连 USAGE(连接权)都不自动给——除非你用 GRANT 显式授权。
- 必须搭配
GRANT才能用,例如:GRANT SELECT ON mydb.* TO 'app'@'192.168.1.10'; -
USAGE是唯一不操作数据的权限,仅表示“允许连接”,但无法查任何表;它常被隐式包含在其他权限中,不单独授予也没问题 - MySQL 8.0+ 不再需要
FLUSH PRIVILEGES——GRANT和CREATE USER会立即生效;老版本(5.7 及之前)仍需手动刷新
GRANT 的权限范围怎么写才安全?
权限范围写错,轻则权限过大,重则暴露整个数据库。关键看三点:数据库名、通配符、主机限制。
-
mydb.*:只对mydb库下所有表授权,最常用也最推荐 -
*.*:全局权限,等同于 root 级别,生产环境禁止给普通用户 -
mydb.users:精确到表,适合敏感表隔离(如只让日志用户读log_table) -
mydb.users(username,email):列级权限,极少用,且不支持所有存储引擎(如 MyISAM 不支持) - 主机部分写成
'%'意味着任意 IP 可连,务必配合防火墙或网络策略;内网建议用'192.168.1.%'或具体 IP
删除用户后权限还在?
现象:执行 DROP USER 'olduser'@'localhost';,但发现另一个同名用户 'olduser'@'%' 还能登录、还能查数据。
根本原因:'olduser'@'localhost' 和 'olduser'@'%' 是两个完全独立的账号,权限、密码、host 限制全部分开存储。MySQL 认证时严格匹配 host 字段,不会 fallback。
- 查看所有用户真实记录:运行
SELECT user, host FROM mysql.user; - 删错 host 容易留后门,尤其注意
'root'@'%'这种高危组合,生产库必须禁用 - 修改密码不要用
UPDATE mysql.user直接改表,要用ALTER USER 'u'@'h' IDENTIFIED BY 'new',否则加密方式可能不兼容
权限变更后应用仍连不上?
不是权限没生效,而是客户端复用旧连接或 DNS 缓存导致认证失败。
- 确认当前连接使用的用户:登录后执行
SELECT CURRENT_USER();(返回'u'@'h')和SELECT USER();(返回登录时用的凭证) - 检查应用是否用了连接池,旧连接未断开;重启应用或清空连接池更可靠
- 如果 host 写的是域名(如
'app'@'web01.example.com'),MySQL 会反向 DNS 解析;确保服务器能解析该域名,或改用 IP 避免依赖 DNS - 某些驱动(如旧版 MySQL Connector/J)缓存权限元信息,升级驱动或加参数
useSSL=false&allowPublicKeyRetrieval=true可缓解
真正麻烦的不是语法记不住,而是 @'host' 这个部分被当成可选、被当成默认值、被当成通配符——它其实是权限体系里最硬的边界。漏掉 host 或写错 host,90% 的权限问题就出在这儿。


















