一条查询语句在 MySQL中到底是如何执行的?

332 阅读3分钟

一个学不会复制粘贴的最佳姿态的小学未毕业生

一个查询语句到底是怎么执行的?

SELECT * FROM table_name WHERE id = 10;

mysql 分为两层

  1. server 层; 连接器, 分析器,优化器, 执行器
  2. 引擎层, innodb, myisam, memory

server 层的工作流程

连接器

负责客户端建立链接, 获取和校验权限, 维持和管理链接

链接是一个比较复杂的过程,尽量链接复用

分析器 -> 生成执行计划

词法分析: 对客户端输入的字符串做解析, 比如识别出这是更新语句还会其他的语句, 把 table_name 识别成表名, id 识别成列名。 先 from 再 join, where, on 等等操作

语法分析 语法分析:判断用户的输入的语句是否满足 MySQL 的语法规则 比如:

1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ELECT COUNT(*) FROM user_infos' at line 1

优化器

对执行计划进行方案的最优评估, 比如是否使用索引啊, 使用哪个索引啊, 等等都是再这里执行的

执行器

获取锁, 打开表, 通过 meta 数据, 获取数据

执行器就是调用引擎层的接口

开始执行的时候, 需要判断链接对这个有没有对应的权限, 如果没有权限, 就会返回没有权限的错误。

在工程实现上, 如果命中查询缓存, 会在查询缓存返回的时候做权限验证。 查询也会在优化器之前调用 precheck 验证权限, 因为在分析器之后, 才知道这个语句需要做什么? 比如查询那张表, 修改哪张表的数据。


mysql> select * from T where ID=10;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有权限, 就打开表继续执行。 打开表的时候, 执行器就会根据表的引擎的定义, 执行器会根据表的引擎定义, 去使用这个表引擎提供的接口。

SELECT * FROM table_name WHERE id = 10;

如果 表 table_name 中, id 字段没有索引, 它是这样执行的。

  1. 调用 innodb 引擎接口, 取表中的第一行, 判断 id 的值是不是 10, 如果不是则跳过, 如果是则将这行存在结果集中。
  2. 调用引擎接口读取下一行, 重复相同的判断逻辑, 知道取到这个表的最后一行。
  3. 执行器将上诉遍历结果组成的记录集作为结果集返回给客户端。

如果 table_name 中, id 字段有索引, 执行过程如下:

  1. 调用 InnoDB 引擎接口取这个表满足条件的第一行
  2. 循环读取,调用满足下一行的接口, 直到不满足条件为止

rows_examined 字段, 表示这个语句执行的过程中, 扫描了多少行。 这个值是在执行器每次调用引擎获取行数的时候累加的。

返回给连接器和用户模块, 清理, 等待新的请求。

问题:

1. 为什么对权限的检查不在优化器之前做?

答: SQL语句要操作的表不是 SQL 字面上的那些, 比如有一个触发器, 得在执行器阶段才能确定, 优化器阶段之前无能为力。 --说多了还是在语句执行之前做不到。