mysql权限管理是分层校验系统,按user→db→tables_priv→columns_priv顺序逐层匹配,高阶权限覆盖低阶限制;grant/revoke操作内存快照,手动改表需flush privileges;'user'@'localhost'与'%'为独立账户;mysql 8.0+推荐用role实现权限收敛。

MySQL 权限管理不是“给用户开个账号就完事”的操作,而是由一套分层校验机制驱动的访问控制系统——它决定谁能在哪台机器上连进来、能看哪些库、能改哪张表、甚至能操作哪一列。
权限检查走的是固定顺序:user → db → tables_priv → columns_priv
MySQL 不是“查一次就定终身”,而是在每次执行 SQL 前按顺序逐层匹配权限表:mysql.user(全局)、mysql.db(库级)、mysql.tables_priv(表级)、mysql.columns_priv(列级)。只要某一层匹配成功且权限足够,就放行;否则继续往下查。但注意:高一级权限会覆盖低一级的限制——比如你在 user 表里被授予了 DELETE,那哪怕 db 表里没开这个权限,你照样能删所有库的任意数据。
GRANT 和 REVOKE 不直接改表,而是操作内存副本
MySQL 启动时把权限表全加载进内存,后续所有权限判断都基于这份快照。所以:
-
GRANT或REVOKE执行后,权限立即生效(无需重启) - 但如果你手动用
UPDATE或INSERT直接改mysql库里的表,权限不会自动刷新——必须显式执行FLUSH PRIVILEGES - 不推荐手改权限表,容易错位字段、破坏主键或触发权限缓存异常
主机名 'user'@'localhost' 和 'user'@'%' 是两个完全独立的账户
这是最常踩的坑。MySQL 认证靠“用户名 + 主机名”联合唯一识别,'dev'@'localhost' 和 'dev'@'192.168.1.%' 即使密码一样,也是两个账户,权限得分别授予。
-
localhost特指本地 Unix socket 连接,不走 TCP;127.0.0.1是 TCP 回环,会被当成远程主机处理 -
%匹配任意非 localhost 主机,但不包含 localhost(这是 MySQL 的历史设计,别问为什么) - 线上环境建议避免用
%,优先写具体 IP 段或域名,防止越权暴露
角色(ROLE)不是“锦上添花”,而是生产环境权限收敛的刚需
MySQL 8.0+ 支持 CREATE ROLE,它本质是权限包的抽象。开发、测试、运维各有一套固定权限组合,与其给 20 个开发每人重复 GRANT SELECT, INSERT ON app.*,不如:
- 先建角色:
CREATE ROLE 'app_dev'; - 批量授权:
GRANT SELECT, INSERT ON app.* TO 'app_dev'; - 再绑定用户:
GRANT 'app_dev' TO 'zhangsan'@'10.0.1.%'; - 启用角色:
SET DEFAULT ROLE 'app_dev' TO 'zhangsan'@'10.0.1.%';
这样权限变更只需改角色定义,不用遍历所有用户——尤其在合规审计场景下,漏掉一个账号就可能触发 HIPAA 或等保整改。
真正难的从来不是“怎么授予权限”,而是“在哪个层级授、对谁授、授完怎么验证没冲突”。权限表之间的覆盖逻辑、主机名匹配的隐含规则、角色与用户状态的分离管理,三者叠加,稍不留神就会出现“明明 SHOW GRANTS 看起来有权限,执行却报 Access denied”的情况。










