mysql权限校验分两阶段:连接器在建连时校验用户身份并缓存权限,预处理器在解析后、优化前校验表/列级权限,执行器在调用引擎前再做字段级二次校验。

连接器先校验权限,不是“连上就查”
客户端发起 mysql -h127.0.0.1 -uroot -p 或 JDBC 连接时,MySQL 并不直接处理 SQL,而是先走连接器流程:完成 TCP 三次握手、验证用户名密码、加载该用户的权限快照(注意:后续权限变更对已存在连接无效)。如果权限不足,会立刻报错 Access denied for user,根本不会进入后续阶段。
常见错误现象:明明在 mysql.user 表里改了权限,但应用仍报权限拒绝——大概率是复用的长连接没重连,也没调用 mysql_reset_connection() 重置上下文。
关键点:
- 每个连接独享一套权限缓存,不随全局权限表实时刷新
- 连接空闲超时由
wait_timeout控制,默认 28800 秒(8 小时) - 连接器分配的是线程(或从线程池取),不是进程;高并发下线程数瓶颈比 CPU 更早出现
解析器和预处理器分两步拆解 SQL
SELECT name FROM user WHERE id = 1 这类语句,先被解析器做词法分析(切出 SELECT、name、FROM 等 token),再构建成语法树;接着预处理器介入,干三件事:检查 user 表是否存在、name 字段是否属于该表、当前用户是否有 SELECT 权限。任一失败,直接报错,比如 Unknown table 'user' 或 SELECT command denied to user。
容易踩的坑:
-
*在预处理阶段就被展开成实际列名,所以SELECT *的字段顺序、个数,完全取决于表定义那一刻的结构 - 字段别名在预处理阶段不可见,
SELECT a AS b WHERE b > 1会报Unknown column 'b' - 跨库查询如
SELECT * FROM db1.t1 JOIN db2.t2,预处理器会分别校验两个库的访问权限
优化器决定“怎么查”,不是“查什么”
优化器拿到合法的语法树后,核心任务是生成执行计划——它不关心你要什么结果,只关心哪条路径代价最低。比如 WHERE age > 25 AND status = 1,优化器可能选 status 索引(因选择性高),也可能全表扫描(若 status 只有两个值,索引区分度低)。
实操建议:
- 用
EXPLAIN看优化器选了哪个索引、是否用到覆盖索引、有没有 Using filesort -
FORCE INDEX能绕过优化器决策,但仅限调试;生产环境慎用,因为统计信息更新后可能反而更差 - 复合索引字段顺序必须匹配
WHERE中的最左前缀,INDEX(a,b,c)对WHERE b = 1无效
执行器调存储引擎,但不直接读磁盘
执行器拿到优化器输出的执行计划后,开始逐行调用存储引擎接口。以 InnoDB 为例:它先查 Buffer Pool 是否有对应页,命中则直接返回;未命中才触发磁盘 I/O 加载页到内存。整个过程对 Server 层透明——执行器只管发指令,不关心数据在哪。
关键细节:
- 执行器会做二次权限校验(比如某列被
SELECT权限限制,但之前预处理只校验了表级权限) -
LIMIT是执行器层控制的,不是存储引擎层提前截断;即使最终只取 1 行,InnoDB 仍可能扫描多行后由执行器丢弃 - 如果 SQL 涉及多个表,执行器按优化器指定的顺序驱动各表,驱动表(outer table)的每一行都会触发被驱动表(inner table)的一次索引查找或全表扫描
执行流程里最容易被忽略的,是预处理器和执行器这两层权限校验的分工:前者管“能不能查这张表”,后者管“能不能查这个字段”,中间还隔着一个优化器对执行路径的黑盒决策。很多权限问题卡在预处理,而性能问题往往藏在执行器与存储引擎的交互细节里。











