mysql.user 表位于 mysql 系统数据库中,存储全局权限、认证信息和账号状态,不可删除;查用户权限需用 SHOW GRANTS,修改须用 ALTER USER 而非直接 UPDATE。

mysql.user 表在哪个数据库里
它就在 mysql 数据库里,是系统自带的、不可删的表。不是你建的库,也不是临时表,所有用户账号(包括 root)的全局权限都存在这里。
常见错误现象:执行 SELECT * FROM user; 报错 Table 'xxx.user' doesn't exist——因为没指定库,MySQL 默认查当前库,而 user 表只在 mysql 库下。
- 必须显式用
SELECT * FROM mysql.user; - 连接时若未指定库,也得先
USE mysql;再查 - 低版本(5.7 及以前)字段名和 8.0 差异大,比如
password字段在 8.0 已被authentication_string替代
查用户权限为什么不能只看 mysql.user
mysql.user 只存全局权限(比如 SELECT_priv、Insert_priv),但实际权限是叠加生效的:库级、表级、列级、甚至存储过程权限,分别存在 mysql.db、mysql.tables_priv、mysql.columns_priv 等表里。
使用场景:你给用户 GRANT SELECT ON test.* TO 'u1'@'%';,他在 mysql.user 里对应字段仍是 N,权限却真实存在——因为记录在 mysql.db 里。
- 查完整权限要用
SHOW GRANTS FOR 'u1'@'%';,它自动聚合所有层级 - 直接查
mysql.user只适合确认是否具备全局 DDL 权限(如Create_priv)、是否允许登录(account_locked)、认证方式(plugin) - 8.0 后新增
mysql.role_edges和mysql.default_roles,角色权限也不在user表里
修改 mysql.user 表会怎样
不建议直接 UPDATE mysql.user。MySQL 不保证这类操作的原子性或一致性,尤其涉及密码哈希、插件字段时,极易导致用户无法登录或权限异常。
典型翻车点:手动 UPDATE authentication_string 值写错格式(比如该用 SHA256_PASSWORD 却填了明文),或漏设 plugin 字段,结果用户连不上,且错误日志只报 Access denied,不提示具体原因。
- 改密码请用
ALTER USER 'u1'@'%' IDENTIFIED BY 'xxx'; - 锁账号用
ALTER USER 'u1'@'%' ACCOUNT LOCK;(比 UPDATEaccount_locked='Y'安全) - 刷新权限必须
FLUSH PRIVILEGES;,但用CREATE/GRANT/ALTER USER语句则自动触发,无需手刷
mysql.user 表结构在不同版本的关键差异
5.7 和 8.0 的 mysql.user 字段数量差了一倍多,8.0 新增了密码过期、历史限制、失败登录跟踪等安全字段,老脚本直连 8.0 很容易因字段缺失或类型不匹配出错。
性能影响:8.0 的 mysql.user 表默认引擎是 InnoDB(5.7 是 MyISAM),支持事务和行锁,但字段变多后,全表扫描更慢;高并发认证场景下,过度依赖 mysql.user 查询可能成瓶颈。
- 5.7 关键字段:
User,Host,password,Select_priv,ssl_type - 8.0 关键新增:
account_locked,password_last_changed,password_lifetime,failed_login_attempts,password_reuse_history - 兼容写法:查用户是否存在,别依赖字段偏移,始终用列名,例如
SELECT Host,User FROM mysql.user WHERE User='root';
真正难的不是找到这张表,而是意识到:它只是权限拼图的第一块,而且每块的形状在升级后都可能变。字段名、默认值、甚至校验逻辑,都在 quietly 改动。











