相信大家和我一样,经常使用SQL查询语句。比如,现在有个很简单的表,表里面只有一个ID字段,在执行下面语句时:
mysql> select * from T where ID=10;
我们看到的只是输入一条语句,返回一个结果,却不知道这条语句在 MySQL 内部的执行过程。
这几天自己查看相关资料,终于搞懂了SQL查询语句的过程,想着自己记录下来,让自己对MySQL有更深刻的了解。
下面是 MySQL 的基本架构示意图,从中我们可以清楚地看到 SQL 语句在 MySQL 的各个功能模块中的执行过程。
通过上图我们大体知道,MySQL可以分为Server层和存储引擎层两部分。
Server层主要包括:连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
而存储引擎层:负责数据的存储和提取。有InnoDB、MyISAM、Memory多种引擎。从 MySQL 5.5.5 版本开始默认引擎是InnoDB。
接下来我们分析一下每个模块主要作用功能:
1.连接器
连接器负责跟客户端建立连接、获取权限、维持和管理连接。MySQL 既支持短连接,也支持长连接。短连接就是操作完毕以后,马上 close 掉。长连接可以保持打开,减少服务端创建和释放连接的消耗,后面的程序访问的时候还可以使用这个连接。
一般我们会在连接池中使用长连接,但是保持长连接比较耗内存。长时间不活动的连接,MySQL 服务器会断开。MySQL默认长连接时间是 28800 秒,8 小时。
2.查询缓存
连接建立完成后,你就可以执行 select 语句了。执行逻辑就会来到第二步:查询缓存。
MySQL将查询语句作为Key,返回结果作为Value,直接缓存到内存中。但是MySQL缓存功能默认是关闭的,这是为什么呢?
主要是因为 MySQL 自带的缓存的应用场景有限,第一个是它要求 SQL 语句必须一模一样,中间多一个空格,字母大小写不同都被认为是不同的的 SQL。
第二个是表里面任何一条数据发生变化的时候,这张表所有缓存都会失效,所以对于有大量数据更新的应用,也不适合。
在 MySQL 8.0 中,查询缓存已经被移除了。
3.分析器
分析器会先对SQL语句做词法分析,词法分析就是把一个完整的 SQL 语句打碎成一个个的单词。
比如一个简单的 SQL 语句:
select name from user where id = 1;
比如通过select就知道我们这是一条查询语句。它也要把字符串“user”识别成“表名 user”,把字符串“name”识别成“列 name”。
做完了这些识别之后,就要做语法分析了。所谓语法分析其实就是判断这条语句是否满足MySQL语法。
4.优化器
经过上面分析器,MySQL已经知道你要做什么了,而且也满足MySQL的语法。但是在开始执行之前,还要经过优化器的处理。
那么优化器可以做什么呢?优化器的作用就是根据解析树生成不同的执行计划(Execution Plan),然后选择一种最优的执行计划,MySQL 里面使用的是基于开销(cost)的优化器,哪种执行计划开销最小,就用哪种。
举一个简单的例子:
1、当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表。
2、有多个索引可以使用的时候,选择哪个索引。
5.执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误,如下所示 (在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。查询也会在优化器之前调用 precheck 验证权限)。
mysql> select * from T where ID=10; ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
比如我们这个例子中的表 T 中,ID 字段没有索引,那么执行器的执行流程是这样的:
1.调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
2.调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
3.执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
至此,这个查询SQL语句就执行完成了。