user表优先级永远高于db表——只要user表中Host和User匹配且权限字段为'Y',后续库级、表级权限检查全部跳过;若user表中对应权限为'N',则直接拒绝,不再检查db表。

MySQL权限校验时,user表和db表谁先生效?
user表优先级永远高于db表——只要user表里某行匹配当前连接的Host和User,且对应权限字段(如Select_priv)为'Y',后续所有库级、表级权限检查都会跳过。这不是“叠加”,而是“短路”。
常见错误现象:GRANT SELECT ON mydb.* TO 'u1'@'%'; 执行后仍报 Access denied for user 'u1'@'192.168.1.100' —— 很可能是因为user表中该用户全局Select_priv是'N',而MySQL压根没走到db表这步。
- 权限校验顺序固定:user → db → tables_priv → columns_priv → procs_priv
-
user表匹配靠Host和User两个字段,支持通配符(如'%'),但注意'localhost'和'127.0.0.1'在MySQL中是不同主机 - 即使你只给
db表授权,也必须确保user表中对应用户的max_questions、max_updates等资源限制不是0,否则连接会被拒绝
为什么SHOW GRANTS看不到db表里的权限?
SHOW GRANTS FOR 'u1'@'%'; 只显示user表和db表中Host与当前登录Host完全一致的记录——如果用户从'192.168.1.100'登录,但db表里只有一条Host='192.168.%'的记录,这条不会出现在SHOW GRANTS结果里,但它依然生效。
使用场景:排查权限不生效时,别只信SHOW GRANTS;直接查表更可靠:
SELECT Host, Db, Select_priv, Insert_priv FROM mysql.db WHERE User='u1' AND Host LIKE '192.168.1.%';
-
db表的Host字段支持模式匹配('%'、'_'),但user表不支持模式匹配——它只做精确匹配或通配符开头的简单比较 -
db表权限仅对显式指定数据库生效,比如USE mydb;之后才能用SELECT,但不能跨库操作(除非user表开了全局权限) - 修改
db表后必须执行FLUSH PRIVILEGES;,否则不生效;而用GRANT语句则自动刷新
权限冲突时,user表里的N能覆盖db表里的Y吗?
能,而且是强制覆盖。比如user表中Select_priv='N',db表中同一用户对mydb有Select_priv='Y',最终结果仍是无查询权。MySQL不会“取并集”,而是按顺序逐层判断,一旦某层返回明确拒绝('N'),立即终止校验。
容易踩的坑:误以为“grant了db权限就一定有权限”,忽略了user表里可能残留旧配置或初始化时被脚本设为全'N'。
-
user表中权限字段为'N'表示“拒绝”,不是“未设置”;空值或NULL在老版本中可能被当作'N'处理 - 创建用户时若用
CREATE USER 'u1'@'%' IDENTIFIED BY 'pwd';,user表所有权限字段默认都是'N',必须显式GRANT或UPDATE才开权限 - 复制环境要注意:
mysql.user表不被复制,主从上权限可能不一致;db表同理,需手动同步或统一用GRANT
如何安全验证某用户在某库的真实权限?
别依赖脑内推演,用SELECT查权限表 + 实际连接测试最可靠。重点看两处:
SELECT Host, User, Select_priv, Insert_priv FROM mysql.user WHERE User='u1' AND Host IN ('%', '192.168.1.100', 'localhost');SELECT Host, Db, User, Select_priv FROM mysql.db WHERE User='u1' AND Db='mydb' AND Host IN ('%', '192.168.1.100');然后用该用户真实连接,执行SELECT USER(), CURRENT_USER();确认实际匹配的是哪一行——CURRENT_USER()返回MySQL用于权限检查的身份,它往往比USER()更关键。
-
CURRENT_USER()显示user表匹配到的具体Host+User组合,这才是权限决策依据 - 权限缓存有延迟:刚改完
mysql.db但没FLUSH,或连接复用旧线程,都可能导致测试结果不准 - 某些客户端(如phpMyAdmin)会自动加
USE mysql;,触发额外权限检查,干扰判断
权限逻辑看着线性,实则每一步都受Host匹配精度、大小写、通配符展开、甚至mysqld启动参数影响。最稳妥的做法,永远是查表 + 看CURRENT_USER() + 换真实IP连一次。


















