深入理解MySQL ———— 一条SQL查询语句是怎样执行的

Posted wtxuebc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解MySQL ———— 一条SQL查询语句是怎样执行的相关的知识,希望对你有一定的参考价值。

文章目录

概述

 平时我们使用数据库,看到的通常是一个整体,比如,你有一个最简单的的表,表里边只有一个ID字段,在执行下面这个查询语句时:

    mysql> select * from Student where ID = 10;

 我们看到的只是输入一条语句,返回一个结果,却不知这条语句在MySQL内部的执行过程。

 所以本篇文章带大伙把MySQL拆解一下,看看里边有哪些“零件”,希望通过本篇,能对MySQL有更深入的理解。这对我们解决MySQL常见的一些异常或问题,就可以直戳本质的分析并解决。

​           MySQL的逻辑架构图

​   MySQL 可以分为 Server 层和存储引擎层两部分。

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

​   而存储引擎层负责数据的存储提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。 就是说在create table建表的时候,如果不指定存储引擎类型,默认使用的就是InnoDB。可以通过指定存储引擎类型来选择别的引擎,比如在create table语句中使用 engine=memory,来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同。InnoDB和MyISAM

​   不同的存储引擎共用一个Server层,也就是从连接器到执行器的部分。

现在就可以结合开头的

mysql> select * from Student where ID=10;

走一遍整个执行流程,依次看下每个组件的作用。

连接器

​   第一步,你会先连接到这个数据库,这时候 “接待“ 你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令中的 mysql是客户端工具,用来跟服务器建立连接。在完成TCP三次握手之后,连接器就要验证开始验证身份,此时用的就是输入的用户名和密码。

​   细节:在mysql -u root -p 之后可以直接写密码,也可以回车输入密码;若直接将密码跟在-p后面可能会导致密码泄漏。如果连的是一些重要的服务器,比如生产服务器,强烈建立不要那么做。

  • 如果用户名密码通过,连接器会到权限列表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都依赖于此时读到的权限。

​   这就是说,一个用户成功连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响到已经存在的连接的权限;只有再建立的连接才会使用新的权限执行。

​   连接完成后,若没有后续的动作,这个连接就处于空闲状态,可以在 show processlist 命令中看到它,客户端若长时间非操作,连接器就会自动将它断开。这个时间是由参数 wait_timeout 决定的(和connection_timeout区分,后者是“连接过程中的等待时间”),默认值是 8 小时。如果在连接被断开之后,客户端再发起操作请求,就会收到一个错误提醒:这时候,如果需要继续操作,就需要重新连接。

  • 长连接:在连接成功后,客户端的请求都用的是同一个连接(建立连接的过程通常是比较复杂的,所以使用过程中尽量减少连接动作,尽量使用长连接
  • 短连接:是指每次执行完很少的几次查询就断开连接,下次查询需要重新建立连接

​  长连接会遇到的问题:MySQL在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开时才释放。长期积累下来,导致内存占用过大,被系统强行终止( OOM ),现象上来看就是MySQL异常重启了。

解决方案

  • 定期断开长连接:使用一段时间后,或程序里边判断执行过一个占用内存较大的查询后,断开连接,之后查询再重连。
  • MySQL5.7以后的版本,可以在每次执行一个较大的操作后,通过执行 mysql_reset_connection 来重新初始化来连接资源,此过程只是将连接恢复到刚连接的状态,而不需要重连和权限验证。

查询缓存

​   连接建立完成后,就可以执行 select 语句了。执行逻辑就来到了第二步:查询缓存

​   MySQL拿到一个查询请求后,会先查询缓存;之前执行过的语句及结果可能会以 key( 查询语句 )-value( 查询结果 ) 的形式,被直接缓存在内存中。如果能够在缓存中找到查询语句的key,那该value就会被直接返回给客户端。

​   如果缓存里边没有 key,就会继续后续的执行阶段。执行完成后,执行结果会被存入查询缓存中。若查询命中缓存,MySQL就不用执行后面的复杂操作,就可以直接返回结果,效率高!

​   但是!大多数情况下不建议使用查询缓存,因为查询缓存往往弊大于利

​   查询缓存的失效非常频繁!只要对一个表更新,这个表上所有的查询缓存都会被清空。或许你努力存的value,还没使用时,就被一个更新给清空了。对于更新压力大的数据库,查询缓存的命中率非常低!查询缓存更适合被更新少的数据库使用。

​   若想让MySQL不使用查询缓存,只需将参数 query_cache_type 设置成 DEMAND,这样对于默认的SQL语句都不使用查询缓存;可以用SQL_CACHE 显式指定的使用查询缓存,例如下语句:

mysql> select SQL_CACHE * from student ehere ID=10;

注:MySQL 8.0版本直接将查询缓存的整块功能删掉了,没有这个功能了;

分析器

​   如果没有命中查询缓存,就要开始执行语句了!首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句进行解析

词法分析

​   分析器会先做词法分析。输入的SQL语句是由多个字符串和空格组成的,那MySQL自然需要识别出里边的字符串分别是什么,代表什么!

mysql> select * from Student where ID=10;

​  以这个SQL语句为例,MySQL首先会识别关键字,如 select ,就说明这是一个查询语句MySQL还需识别 student 是表的名字,识别 ID 为列 ID

语法分析

​   词法分析后进行语法分析,在将各个字符串 “ 词汇 ” 认出来后,还要进行语法判断,看起是否满足MySQL的语法;在语法出现错误后,MySQL就会报错,一般错误会提示第一个出现该错误的位置,因此只需根据提示进行相关修改即可

注:若是分析器分析出来更新操作,更新完之后查询缓存失效

优化器

​   通过分析器,明白你要干啥后,数据库就要针对你的需求想一个最优的解决方案,也就是执行计划,这个最优方案选择的操作,这个就是优化器要做的事情了。

​   优化器是在表有多个索引的时候,决定用哪个索引;或者在一个基本语有多表关联 ( join )的时候,决定各个表的连接顺序;

​ 如下例子:

  mysql> select * from t1 join t2 using(ID)  where t1.c=10 and t2.d=20;

​   此时既可以用 t1表里边的 c=10 记录的 ID 的值,再根据ID值关联到 t2 ,去判断 d等于20否;

​   也可以用 t2 表里边的 d=20 ,记录的 ID 的值,再根据ID值关联到 t1,去判断 c等于10否;

​   这两种方法的逻辑结果是一样的,但是执行效率会有不同!而优化器的作用就是选择使用哪种方法!

执行器

​  优化器阶段完成后,SQL语句的执行方案就确定下来了,然后进入执行方案;也就是说,通过分析器知道了你要干啥,通过优化器知道了该怎么做,这下进入执行阶段开始执行。

​   首先要验证权限,若权限不足,返回错误;

​   注: 权限验证不仅仅在执行器这部分会做,在分析器之后,也就是知道了该语句要“干什么”之后,也会先做一次权限验证。叫做precheck。而precheck是无法对运行时涉及到的表进行权限验证的,比如使用了触发器的情况。因此在执行器这里也要做一次执行时的权限验证。 如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证;

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

​   所以到了执行的时候才会进入到数据库引擎,然后执行器也是通过调用数据库引擎的API来进行数据操作的。也因此数据库引擎才会是插件形式的。

​   执行器会先调用 **“满足添加的第一行”**这个接口,之后循环取 **“满足条件的下一行”**这个接口,这些接口都是引擎定义好的;

​   数据库的慢查询日志中有一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行

​   注意:rows_examined跟引擎扫面行数并不是完全相同的。前者是执行器执行的行数,后者是引擎扫描的总行数。有些场景下:执行器调用一次,在引擎内部则扫描了多行。

存储引擎层的各种数据获取方法都是已经定义好了的; 优化器 生成的执行计划,决定了 执行器会选择 存储引擎的哪个方法去获取数据;

总结

​ 本篇文章是通过一个简单的 SQL 语句对完整执行流程的各个阶段进行了一个初步的认识,当然这每个阶段每个模块又任重而道远,笔者会在后续的文章里对其模块进行深入总结。


参考文献: 林晓斌《MySQL实战 45 讲》

以上是关于深入理解MySQL ———— 一条SQL查询语句是怎样执行的的主要内容,如果未能解决你的问题,请参考以下文章

深入理解SQL原理:一条SQL查询语句是如何执行的?

深入理解MySQL ( 2 )————一条更新语句是如何执行的

深入理解MySQL ( 2 )————一条更新语句是如何执行的

深入理解MySQL ( 2 )————一条更新语句是如何执行的

通过一条语句的执行,深入理解innoDB的底层架构

从头开始搞懂 MySQL(01)如何执行一条 SQL 查询语句