MySQL 的权限存储在哪张表里
MySQL 的 ACL(Access Control List)信息全部存放在
mysql系统数据库的几张核心表中,不是内存结构或配置文件。主要涉及:
user、
db、
tables_priv、
columns_priv、
procs_priv和
proxies_priv。
其中最常用的是:
user:控制全局级别权限(如
SELECT、
CREATE USER)和连接参数(如
max_connections)
db:控制某个数据库级别的权限(如对
myapp库的
INSERT)
tables_priv和
columns_priv:细化到表或列的权限,实际生产中极少手动操作,多通过
GRANT语句间接写入
直接
UPDATE这些表不会立即生效——必须执行
FLUSH PRIVILEGES才能重载,否则新权限不被识别。
GRANT 和 INSERT INTO mysql.user 的区别
用
GRANT创建用户并赋权,是 MySQL 推荐且安全的方式;而直接
INSERT INTO mysql.user是绕过校验的“硬写”,容易出错。
关键差异:
GRANT会自动哈希密码(如果使用
IDENTIFIED BY)、检查语法、填充默认权限字段(如
ssl_type、
account_locked),并隐式触发权限重载 手动
INSERT必须自己设置
authentication_string(不是
password字段,5.7+ 已废弃)、
plugin(通常是
caching_sha2_password或
mysql_native_password),漏掉
account_locked或
password_expired可能导致用户无法登录
GRANT ... ON *.*写入
user表;
GRANT ... ON db.*写入
db表;但直接
INSERT不会自动关联
db表,权限粒度会错位
例如,以下语句看似等价,实则风险不同:
GRANT SELECT ON mydb.* TO 'appuser'@'10.0.1.%' IDENTIFIED BY 'pwd123';
vs
INSERT INTO mysql.user (Host, User, authentication_string, plugin) VALUES ('10.0.1.%', 'appuser', PASSWORD('pwd123'), 'mysql_native_password');后者没设
Select_priv = 'Y',也没插入
db表记录,用户创建成功但无任何权限。
权限生效顺序与拒绝优先级
MySQL 权限按层级叠加,但“拒绝”(
DENY)在 8.0+ 才支持,且只在企业版或 8.0.16+ 社区版通过
REVOKE+
WITH ADMIN OPTION间接体现;标准社区版没有显式
DENY机制。
权限判断逻辑是“匹配最长 Host/User + 最高粒度”,顺序固定为:
先查user表(全局)→ 匹配
Host和
User,满足则用该行权限 若全局无匹配,再查
db表 → 要求
Host、
User、
Db全部匹配 再往下是
tables_priv、
columns_priv,逐级细化
注意:
user表中某权限字段为
'N',不代表“拒绝”,只是“未授予”。MySQL 没有 deny-by-default 模式,所有未显式授予的权限默认不可用。
一个常见陷阱:
user表里
Select_priv = 'Y',但
db表里同用户对某库
Select_priv = 'N'—— 实际仍可查(因为全局权限覆盖库级)。只有当全局为
'N',才继续下探。
FLUSH PRIVILEGES 什么时候必须执行
仅在**直接修改
mysql系统表**后才需要
FLUSH PRIVILEGES;用
GRANT、
CREATE USER、
DROP USER等 DDL 语句操作时,MySQL 自动重载权限缓存,无需手动刷新。
误用
FLUSH PRIVILEGES的后果: 它强制重载全部权限数据,对高并发实例可能造成短暂锁表(尤其是
user表) 如果此时系统表有损坏或格式错误(比如
authentication_string值非法),会导致整个权限系统不可用,连
root都可能无法登录 在 MySQL 8.0+,部分字段(如
password_last_changed)由内部机制维护,手动
FLUSH可能干扰其状态
所以,除非你明确改了
mysql.user或
mysql.db的行,否则不要碰
FLUSH PRIVILEGES。很多线上故障就源于运维脚本里无脑加这一句。
