mysql权限校验在sql执行前分层逐级完成:先查mysql.user(连接与全局权限),再查db(库级)、tables_priv(表级)、columns_priv(列级);任一层匹配即放行,否则报error 1142并终止流程,不进入优化器或执行器。

权限判定发生在SQL执行前,不是运行时检查
MySQL在语句真正执行前就完成权限校验,且是分层逐级匹配的:先查mysql.user表确认连接身份和全局权限,再按需查mysql.db(库级)、mysql.tables_priv(表级)、mysql.columns_priv(列级)。哪怕你只查SELECT name FROM users,只要columns_priv里没开name列的SELECT权限,就会立刻报ERROR 1142 (42000): SELECT command denied to user,根本不会走到优化器或执行器阶段。
常见误解是“能连上就能查”,其实连接成功只说明user表校验通过;后续任何DML/DDL操作都必须再次过权限表。比如GRANT SELECT ON finance_db.* TO 'app'@'%'之后,如果忘了FLUSH PRIVILEGES或客户端没重连,新授权仍不生效——因为权限是会话级缓存的。
列级权限被拒绝时错误信息不提示具体列名
当用户对某张表有SELECT权限但缺少特定列的访问权时,MySQL报错仍是笼统的SELECT command denied,不会指出是哪一列被拦。这是因为权限检查在解析器之后、执行器之前,而列名合法性(如是否存在)本身属于解析器职责;列权限属于更细粒度的访问控制,错误归类统一到语句类型层面。
- 验证方式:用
SELECT * FROM mysql.columns_priv WHERE User='xxx' AND Table_name='yyy';确认是否有对应记录 - 列权限优先级高于表权限:即使
tables_priv给了全表SELECT,只要columns_priv显式拒绝某列,该列仍不可读 - 注意大小写:MySQL 8.0 默认大小写敏感,
name和NAME在columns_priv里算两个不同列
为什么SHOW GRANTS看不到列级权限
SHOW GRANTS FOR 'u'@'h'默认只显示user、db、tables_priv层级的授权语句,columns_priv和procs_priv这类细粒度权限不会出现在输出中。这不是遗漏,而是MySQL设计如此——这些权限无法用标准GRANT语法表达,只能直改系统表或用PROCEDURE ANALYSE()等间接方式推断。
实际排查时得手动查表:
SELECT Host, User, Table_name, Column_name, Select_priv FROM mysql.columns_priv WHERE User = 'app_reader' AND Host = '%';
另外,GRANT语句本身不支持列级授权(如GRANT SELECT(name) ON t TO u是非法语法),必须用INSERT INTO mysql.columns_priv并配合FLUSH PRIVILEGES,生产环境极少这么干,多数靠应用层字段过滤替代。
权限拒绝发生在连接建立后、语句解析完成时
整个流程顺序很关键:连接器→解析器→权限检查→优化器→执行器。这意味着:
- 语法错误(如
SELCT拼错)会在解析器阶段报错,ERROR 1064,此时权限还没开始查 - 权限不足报
ERROR 1142,一定是在解析成功之后、执行之前 -
EXPLAIN能跑通,不代表能执行:它只走解析+优化,不触发权限校验;真正SELECT时才卡在权限环节 - 使用
FORCE INDEX或USE INDEX不影响权限判断路径,它们只干预优化器决策
最易忽略的一点:权限检查依赖当前会话的USER()和CURRENT_USER()。前者返回连接时声明的用户名@host,后者返回mysql.user中匹配到的实际账号。两者不一致时(比如用了代理用户),权限以CURRENT_USER()为准——这也是为什么有时SHOW GRANTS结果和实际行为不符。











