权限校验发生在SQL解析器之后、优化器之前的预处理阶段,按user→db→tables_priv→columns_priv顺序逐级检查,任一级权限为Y即终止检查,否则报ERROR 1142。

MySQL 在执行 SQL 前就完成权限校验,不是“为了安全才加的环节”,而是整个执行流程中不可跳过的一环——它发生在解析器之后、优化器之前,且一旦失败直接报错退出,根本不会走到执行器。
权限校验实际发生在哪个阶段?
不是在连接时一次性校验完所有可能操作,也不是等语句真要读数据才查;而是在语句通过词法/语法分析(即 SELECT * FROM t 被识别为合法 SELECT 语句)后、进入优化器前,立即触发权限检查链。
这个阶段会按固定顺序扫描权限表:user → db → tables_priv → columns_priv。只要某一级返回 Y,就终止后续检查;全为 N 才最终报错。
- 比如你执行
SELECT phone FROM users,即使users表存在、phone字段也存在,只要columns_priv里没开该列的SELECT权限,就会立刻报ERROR 1142 (42000): SELECT command denied to user -
EXPLAIN对这种语句无效——它还没走到优化器,压根不生成执行计划 - 错误信息里带
@'192.168.1.100',说明校验依据是「用户名+来源 IP」组合,不是单看user名
为什么 FORCE INDEX 有时不起作用?和权限无关但常被误判
看到 FORCE INDEX (idx_created_at) 没生效,第一反应不该是“权限没给够”,而是优化器基于成本估算主动弃用了它——这和权限校验完全不在一个流程里。
真正影响是否走索引的是统计信息和选择性:
- 用
SHOW TABLE STATUS LIKE 'orders'查Rows,再算SELECT COUNT(DISTINCT created_at) / COUNT(*) FROM orders,如果结果 -
ANALYZE TABLE orders可刷新统计信息,但不能强制改写优化器决策 -
FORCE INDEX不是断言,只是建议;当优化器判断“走索引更慢”时,它会忽略
动态拼接 SQL 时权限校验为何总失败?
在存储过程中用 CONCAT 拼接用户输入再 EXECUTE,权限校验失败往往不是因为没授权,而是因为执行上下文变了。
- 若过程定义为
SQL SECURITY DEFINER,则以DEFINER账号权限运行,但拼接出的 SQL 仍受调用者能访问的库/表/列限制——除非DEFINER本身有对应权限,且未被sql_mode=STRICT_TRANS_TABLES等拦住 - 更常见问题是:拼接时没用
QUOTE()包裹值,导致注入或语法错误,使语句卡在解析器阶段,根本进不了权限校验 - 推荐做法:权限字段名(如
tenant_id)必须来自白名单列表,值必须经QUOTE()处理,且上线前用SELECT @sql打印核对
权限改了但查询还是报错?缓存和会话级绑定是关键
修改 mysql.user 或 mysql.db 后立刻执行 SQL 却仍报权限不足,大概率是因为权限已会话级缓存,没刷新。
-
FLUSH PRIVILEGES可重载权限表,但更稳妥的做法是让客户端重连——新连接会重新走完整校验链 -
wait_timeout默认 8 小时,长连接空闲太久被服务端断开,应用若没重连逻辑,下次请求就会报MySQL server has gone away,容易误以为是权限问题 - 用
SHOW PROCESSLIST只能看到当前连接的User和Host,看不到具体权限——权限不随查询实时查表,而是连接建立时一次性加载并缓存
权限校验不是黑盒开关,它嵌在语句生命周期里,且每一级都可独立生效或拦截。最易被忽略的是:列级权限(columns_priv)的粒度比想象中细,而会话级权限缓存又比想象中顽固。


















