MySQL 权限按匹配优先级覆盖而非继承:全局(mysql.user)

MySQL 中权限是按层级叠加的,不是继承
MySQL 没有传统意义上的“权限继承”——比如给 db1.* 授予 SELECT,不会自动让 db1.table1 获得额外权限;它只是把权限记录在 mysql.db 表里,作用范围更窄的权限(如表级、列级)会覆盖更宽泛的同名权限。真正起作用的是「匹配优先级」:MySQL 在验证权限时,按 host, user, db, table_name, column_name 从左到右逐级匹配,越具体的记录优先级越高。
- 全局权限(
GRANT …… ON *.*)写入mysql.user,优先级最低 - 数据库级(
ON db1.*)写入mysql.db,优先级中等 - 表级(
ON db1.t1)写入mysql.tables_priv,优先级更高 - 列级(
ON db1.t1(col1))写入mysql.columns_priv,优先级最高
权限冲突时,MySQL 怎么决定用哪条记录?
当用户执行 SELECT col1 FROM db1.t1,MySQL 会同时查多张权限表,并取各层级中「匹配且启用」的最高优先级记录。关键点在于:不是“叠加”,而是“取最大有效权限”。例如:
- 用户
'u1'@'localhost'在mysql.user有SELECT全局权限 → 允许查所有库 - 同一用户在
mysql.db对db1显式DENY SELECT(通过GRANT USAGE+ 单独REVOKE实现)→ 实际拒绝查db1 - 再在
mysql.tables_priv对db1.t1单独GRANT SELECT→ 此时仅对t1恢复可查
最终效果:查 db1.t2 报错,查 db1.t1 成功——因为表级权限覆盖了库级 DENY。
REVOKE 不一定能撤销你认为的权限
REVOKE 只能撤销「当前 作用域 内显式授予的权限」,无法影响更高或更低层级的权限记录。常见误操作:
- 在
db1.*上GRANT SELECT,然后在db1.t1上REVOKE SELECT→ 有效,t1权限被清除 - 在
*.*上GRANT SELECT,然后在db1.*上REVOKE SELECT→ 无效!因为全局权限仍存在,用户依然能查db1 - 想彻底禁用某用户对某个库的访问,必须确保:全局无权限 + 库级无权限 + 表级 / 列级也无残留记录
检查方式:
SELECT host,user,db,table_name,column_name,Select_priv FROM mysql.db WHERE user='u1' AND db='db1'; SELECT host,user,db,table_name,column_name,Select_priv FROM mysql.tables_priv WHERE user='u1' AND db='db1';
FLUSH PRIVILEGES 不是万能刷新开关
手动修改 mysql.* 系统表后,必须执行 FLUSH PRIVILEGES 才能让内存缓存更新;但用 GRANT/REVOKE 命令操作时,MySQL 自动完成持久化和加载,此时再执行 FLUSH PRIVILEGES 是冗余的,甚至可能掩盖权限未生效的真实原因(比如忘记 GRANT 后的 IDENTIFIED BY 密码不匹配)。
- 只在直接
INSERT/UPDATE系统表后才需要FLUSH PRIVILEGES - 使用
GRANT后权限立即生效(连接重连后可见),无需刷新 - 如果
GRANT后权限不生效,先确认user和host是否完全匹配(注意'u1'@'%'和'u1'@'localhost'是两个不同账户)
权限生效依赖于连接时认证的 user@host 组合,这个组合一旦建立就不会再动态切换权限上下文——改完权限后,已有连接不会自动更新权限,必须断开重连。