用户登录
用户注册

分享至

荐 【MySQL】一条查询语句在MySQL内部的执行过程

  • 作者: 黑人不一样的白
  • 来源: 51数据库
  • 2021-09-01

我们几乎每天都在写SQL语句,当我们写了一条查询语句,会得到一个查询结果,但是它在MySQL内部是怎样的一个执行过程呢?

mysql> select * from T where Id = 10;

如图,我们先了解一下MySQL的基本架构示意图。

整体来看,MySQL分为Server层存储引擎层两部分。

Server层包括连接器、查询缓存、分析器、优化器、执行器等,包含了大多数核心功能,以及所有内置函数(如日期、时间、数学和加密函数等),所有涉及到跨存储引擎的功能都在这一层实现,如存储过程、触发器、视图等

存储引擎层负责数据的存储和提取。它的架构模式是插件式的,支持InnoDB、MyISAM、Memory等。我们最多使用的是InnoDB,因为它是MySQL 5.5.5版本后的默认存储引擎。

从图中可以看出,多种存储引擎公用一个Server层,也就是从连接器到执行器的部分。

连接器

首先,会连接到数据库上,这时候就是连接器起的作用。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令:

mysql -h$ip -P$port -u$user -p

输完命令之后,需要输入密码。

连接命令中的mysql是客户端的工具,用来跟服务端建立连接。在完成经典的TCP握手之后,连接器就开始认证身份,也就是我们输入用户名和密码后。如果用户名或密码不对,就会报错“Access denied for user”的错误,这将结束客户端的执行;如果用户名和密码认证通过,连接器会到权限表里面查出你拥有的权限,然后这个链接里面的权限判断逻辑,都将依赖于此时读到的权限。

这意味着,当一个用户成功建立连接后,及时对这个用户的权限做了修改,也不影响当前连接着的权限,只有再次新建连接时才会使用新的权限设置。

连接完成后,如果没有其他操作,该连接处于空闲状态,如图:

如果客户端很长时间没有操作,连接器会自动断开。这个时间可由参数wait_timeout控制,默认是8小时。

数据库里的 长连接 是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接 则指的是每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。因为建立连接的过程比较复杂,所以建议尽量使用长连接。

查询缓存

连接建立完成后,就可以执行select语句了。也就是:查询缓存。

MySQL拿到一个查询请求后,会先去查询缓存看看之前是不是执行过这个语句。如果执行过,其结果会以key-value对的形式被直接缓存在内存中。如果能够找到缓存中的key,那么这个对应的value就会被直接返回给客户端。

如果不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。

虽然使用查询缓存的效率很高,但实质上使用这种方式不太好。因为查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能存了很多结果,还没使用,就被一个更新清空了…

分析器

如果没有命中查询缓存,就该真正执行这个语句去查询了。

首先,mysql需要知道这条语句是要做什么,因此需要对SQL语句进行解析。首先分析器会做“词法分析”。MySQL需要识别我们输入的多个字符串和空格分别代表什么。MySQL从输入的“select”关键字识别出这是一个查询语句,“T”识别成表名“T”,“ID”识别成“列ID”。然后进行“语法分析”。语法分析器会根据语法规则,判断输入的这个SQL是否满足MySQL语法。
如果语句不对,会看到报错You have an error in your SQL syntax提示。

优化器

经过分析器的分析后,MySQL知道要做什么了。开始执行前,还要进行优化器的处理。

优化器是在表里面有多个索引时,决定使用哪个索引;或者在一个语句有多表关联时,决定各个表的连接顺序。比如有一个两表关联的join:

mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
  • 既可以先从表t1里取出c=10的记录的ID值,再根据ID值关联到表t2,再判断t2里面的d的值是否等于20
  • 也可以先从表t2里取出d=20的记录的ID值,再根据ID值关联到表t1,再判断t1里面的c的值是否等于10

这两种执行方法的结果一样,但是执行效率可能不同。优化器的作用就是选择一种效率更高的方式执行。

优化器阶段完成后,这个语句的执行方案就确定了,然后开始进入执行阶段。

执行器

开始执行的时候,要先判断一下当前用户有没有查询表T的权限。如果没有,就会返回没有权限的错误:

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

比如在表T中,ID字段没有索引,那么执行器的执行流程是这样的:

  1. 调用InnoDB引擎接口取该表第一行,判断ID值是不是10,如果不是则跳过,如果是则将这行存在结果集中;
  2. 调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
  3. 执行器将上面遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

这条语句到这就执行完成了。

对于有索引的表,执行逻辑差不多。第一次调用的是“取满足条件的第一行”这个接口,之后循环取“满足条件的下一行”这个接口,这些接口在引擎中已经定义好了。

在数据库的慢查询日志中看到的rows_examined字段表示这个语句执行过程中扫描了多少航。这个值就是执行器每次调用执行引擎获取数据行的时候累加的。

软件
前端设计
程序设计
Java相关