MySQL中系统权限(如CREATE USER)作用于实例全局,ON后只能用.或省略;对象权限(如SELECT)必须显式指定作用域(如db.*、tbl),混用会报错,回收需完全匹配授权语句。

MySQL 中的 GRANT 语句怎么区分系统权限和对象权限?
系统权限(如 CREATE USER、RELOAD、SHUTDOWN)作用于整个 MySQL 实例,不绑定具体数据库;对象权限(如 SELECT、INSERT、ALTER)必须指定作用范围:全局(*.*)、库级(db_name.*)、表级(db_name.table_name)或列级(需在括号中列出列名)。
关键区别在于 ON 子句后的语法:
– 系统权限只能用 ON *.* 或省略 ON(仅限部分旧版本兼容写法,不推荐);
– 对象权限必须显式声明作用域,否则报错 ERROR 1221 (HY000): Incorrect usage of DB GRANT and GLOBAL PRIVILEGES。
常见误操作:
- 对用户授予
SELECT却写成GRANT SELECT ON *.*→ 实际给了所有库表的查询权,远超预期 - 想授库级权限却漏掉点号,写成
GRANT SELECT ON mydb→ 语法错误,必须是mydb.* - 混合使用:一条
GRANT里既含系统权限又含对象权限(如GRANT CREATE USER, SELECT ON test.*)→ 直接报错,必须拆成两条
如何安全地回收权限而不影响其他用户?
REVOKE 的粒度必须与 GRANT 完全一致,包括权限类型、作用域、用户名和主机名。MySQL 不支持“模糊回收”或“按通配符批量撤销”。
例如,若执行过:
GRANT INSERT, UPDATE ON sales.* TO 'app'@'10.20.%';
那么必须用完全相同的主体撤销:
REVOKE INSERT, UPDATE ON sales.* FROM 'app'@'10.20.%';
如果只写 REVOKE INSERT ON sales.* FROM 'app'@'%',会失败——主机名不匹配;如果漏掉 UPDATE,则该权限仍保留。
注意:REVOKE ALL PRIVILEGES 只撤销显式授予的权限,不会动默认角色权限(8.0+)或继承自角色的权限,需额外处理角色。
为什么 SHOW GRANTS FOR 'u'@'h' 显示的权限和实际不符?
常见原因有三个:
- 用户有多个主机段匹配(如同时存在
'u'@'10.20.1.%'和'u'@'%'),MySQL 按“最具体”规则选匹配项,但SHOW GRANTS默认只显示第一个(按mysql.user表顺序),可能不是当前连接实际生效的那条 - 启用了角色(
SET DEFAULT ROLE),而SHOW GRANTS默认不显示角色所带权限,需加FOR 'u'@'h' USING role_name查看角色权限 - 权限缓存未刷新:修改
mysql.user表直改后未执行FLUSH PRIVILEGES,或使用GRANT/REVOKE后因网络中断导致部分节点未同步(集群环境下)
验证当前会话真实权限,用:
SELECT * FROM INFORMATION_SCHEMA.ROLE_TABLE_GRANTS WHERE GRANTEE = "'u'@'h'";(含角色)或直接测试语句(如
SELECT COUNT(*) FROM mysql.user;)更可靠。
MySQL 8.0+ 的角色机制对权限管理有什么实际影响?
角色本质是“权限容器”,本身不登录,必须被用户 SET ROLE 激活才生效。它解决的是权限复用问题,但引入了新复杂度:
- 一个用户可拥有多个角色,但只有
ACTIVE角色的权限计入当前会话;默认角色需用SET DEFAULT ROLE显式指定,否则每次连接后要手动SET ROLE -
GRANT角色时,必须用TO指定用户,不能像对象权限那样用ON绑定范围 —— 角色权限的作用域在创建角色时就固定了 - 角色可以嵌套(
GRANT r1 TO r2),但深度无硬限制,排查权限来源时容易绕晕;建议用SELECT * FROM mysql.role_edges;查依赖链
生产环境启用角色前,务必确认应用连接池是否支持自动执行 SET ROLE(多数不支持),否则权限会降级为仅有 USAGE。










