在使用mysql时,我们只懂得输入一条查询语句,返回结果,却不知道一条查询语句时如何执行的。
假如,你有个最简单的表,表里只有一个ID字段,比如执行下面这个查询语句时:
select * from T where ID=10;
接下来我将拆解一条查询语句的大致执行流程,如下我给出的是Mysql的基本架构图:
总体而言,MySQL可以分为两个主要部分:Server层和存储引擎层。
Server层包括连接器、查询缓存、分析器、优化器和执行器等,涵盖了MySQL的核心服务功能。它还包括所有内置函数(如日期、时间、数学和加密函数等),并实现了所有跨存储引擎的功能,如存储过程、触发器和视图等。
存储引擎层负责数据的存储和提取。MySQL的架构支持多个存储引擎,包括InnoDB、MyISAM、Memory等。目前最常用的存储引擎是InnoDB,从MySQL 5.5.5版本开始成为默认存储引擎。
换句话说,当你执行CREATE TABLE建表语句时,如果没有指定引擎类型,MySQL将默认使用InnoDB引擎。当然,你也可以通过指定存储引擎类型来选择其他引擎,例如在CREATE TABLE语句中使用ENGINE=MEMORY来指定使用内存引擎创建表。不同的存储引擎具有不同的数据存取方式和支持的功能。
从图中可以看出,一个server层供多个不同的存储引擎使用。接下来讲详细解释server层中的每个部件是如何执行的。
1.连接器
第一步,连接器负责跟客户端建立连 接、获取权限、维持和管理连接。连接命令一般写作
mysql -h$ip -P$port -u$user -p
其中的命令解释如下
- $ip: MySQL服务器的IP地址或主机名。
- $port: MySQL服务器监听的端口号。
- $user: 要进行身份验证的用户名。
- -p: 此标志会提示您输入指定用户的密码。
建议不要再-p后明文书写密码
在连接命令中,"mysql"是一个客户端工具,用于与服务器端建立连接。在经典的TCP握手完成后,连接器会开始验证您的身份,使用您输入的用户名和密码进行认证。
如果用户名或密码不正确,您将收到一个"Access denied for user"的错误消息,并且客户端程序将终止执行。
如果用户名和密码认证成功,连接器将从权限表中查找您拥有的权限。随后,在这个连接中进行的权限判断将依赖于此时读取的权限信息。
这意味着,一旦用户成功建立连接,即使管理员账号修改了该用户的权限,已经存在的连接的权限也不会受到影响。只有在创建新连接时才会使用新的权限设置。
连接建立完成后,如果没有后续操作,该连接将处于空闲状态。
2.查询缓存
查询缓存这个功能在mysql8.0之后就被去掉了,原因是这个功能弊大于利,在一些情况下命中率非常低,因此也不建议使用。接下来简单提一提。
MySQL在接收到查询请求后,首先会检查查询缓存,看之前是否执行过相同的查询语句。之前执行过的语句及其结果可能会以key-value对的形式直接缓存在内存中。如果查询语句在缓存中存在,那么查询结果会直接从缓存中返回给客户端。
如果查询语句不在查询缓存中,MySQL会继续执行后续的查询阶段。待查询执行完成后,执行结果会被存入查询缓存中。这样,下次如果有相同的查询请求,就可以直接从缓存中获取结果,避免了复杂的查询操作,从而提高了查询的效率。
通过查询缓存的利用,MySQL能够快速响应那些已经执行过的查询请求,而无需再次执行相同的查询操作,从而提升了整体的查询性能。
既然能够提升性能,那为何开头说不建议使用呢,其原因如下:
查询缓存在MySQL中的失效频率很高,只要对某个表进行更新操作,该表上的所有查询缓存都会被清空。因此,即使你费心地将查询结果存入缓存中,但在实际使用之前就可能被一个更新操作全部清空。对于更新频繁的数据库来说,查询缓存的命中率会非常低。除非你的业务场景是针对某个静态表进行查询,且该表的更新频率很低。例如,一个系统配置表,因为该表很长时间才会更新一次,所以适合使用查询缓存。
3.分析器
当查询缓存未命中时,MySQL开始执行SQL语句。首先,MySQL需要了解你的意图,因此需要对SQL语句进行解析。
解析过程首先进行词法分析。你输入的SQL语句由多个字符串和空格组成,MySQL需要识别这些字符串代表的含义。例如,MySQL会识别出关键字"select"表示这是一个查询语句,将字符串"T"识别为表名"T",将字符串"ID"识别为列名"ID"。
完成词法分析后,MySQL进行语法分析。根据词法分析的结果和语法规则,语法分析器判断你输入的SQL语句是否符合MySQL的语法规范。
如果输入的语句不正确,你将收到一个错误提示,例如"You have an error in your SQL syntax",这表明你的SQL语句存在语法错误。例如,如果输入的语句是"select"缺少了开头的字母"s",就会触发这个错误。
通过解析和语法分析,MySQL能够识别出SQL语句的结构和含义,从而为后续的执行阶段做准备。
4.优化器
经过分析器,MySQL能够确定你要执行的操作。在开始执行之前,还需要经过优化器的处理。
优化器的作用是在表中有多个索引时,决定使用哪个索引;或者在一个语句中涉及多个表的关联(join)时,决定各个表的连接顺序。例如,当执行以下语句时:
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。
虽然这两种执行方法的逻辑结果是一样的,但是它们的执行效率可能不同。优化器的作用就是决定选择使用哪种执行方法,以提高查询的效率。
5.执行器
MySQL通过分析器确定要执行的操作,然后通过优化器确定如何执行操作,进入执行器阶段开始执行语句。
在执行之前,执行器首先要判断用户对表T是否具有查询权限。如果没有权限,将返回相应的错误信息,例如:
mysql> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
对于没有索引的表T,执行器的执行流程如下:
- 调用InnoDB引擎接口获取表的第一行数据,并判断其ID值是否为10。如果不是,则跳过该行;如果是,则将该行加入结果集。
- 调用引擎接口获取下一行数据,并重复相同的判断逻辑,直到获取到表的最后一行。
- 执行器将满足条件的所有行组成的结果集返回给客户端。
对于有索引的表,执行逻辑类似。首次调用的是"获取满足条件的第一行"接口,然后循环调用"获取满足条件的下一行"接口。这些接口已经在引擎中定义好。
在数据库的慢查询日志中,可以看到一个名为"rows_examined"的字段,表示执行过程中扫描了多少行。这个值是在执行器每次调用引擎获取数据行时累加的。需要注意的是,在某些情况下,执行器调用一次接口,引擎内部可能会扫描多行数据,因此引擎扫描的行数与"rows_examined"可能不完全一致。
关于一条查询语句的执行流程大致介绍到这里,本人只是个MySQL学习者,以上是本人通过阅读极客时间的《MySQL45讲》总结出的读书笔记,如有错误,请在评论区回复或私信作者。