MySQL数据库做为优秀的关系型开源数据库,受到全部人的青睐,咱们平常工做中,和其打交道的次数十分频繁,那么你知道咱们在写sql的时候,MySQL内部是如何执行的吗?接下来咱们就一块儿探讨一下吧。mysql
以 select * from T where ID=10;
这条sql语句为例:
当咱们执行该查询语句时,会收到MySQL给咱们返回的结果,那么其在MySQL中是如何一步步执行的呢?
首先咱们了解一下MySQL的架构图,看一下MySQL服务的组织结构:redis
如上图所示:
MySQL大体能够分为 Server 层和存储引擎层两部分。
Server 层包括链接器、查询缓存、分析器、优化器、执行器等,以及全部内置的函数(如日期、时间、数学和加密函数等),全部跨存储引擎的功能都在这一层实现,如存储过程、触发器、视图等。
存储引擎层则负责数据的存取。架构是插件式的,支持 InnoDB、MyISAM、Memory等多个存储引擎。MySQL 5.5.5版本以后,其默认存储引擎为InnoDB。
全部的存储引擎共用一个Server层。sql
预埋问题:memory 引擎和 redis 二者的区别
平时咱们命令行链接MySQL数据库时的命令为:mysql -h127.0.0.1 -P6293 -uroot -p
这条命令就是走到了链接器去验证用户身份。
其中命令中的 mysql
是客户端工具,用来和服务端创建链接。在完成TCP握手以后,链接器开始使用输入的参数信息进行身份认证。数据库
Access Denied for user
的错误,客户端中断。此时咱们应该就会理解,为何咱们给某个用户修改了权限以后,已经存在的该用户的链接不会变动为新的权限。要想新的权限生效,只能新建该用户的链接。api
链接完成后,若没有后续的动做,该链接就处于空闲状态,可经过show processlist
命令查看全部链接状态。其中 Command
列显示为 Sleep
表示系统中有一个空闲链接。缓存
客户端若是长时间处于Sleep,链接器会将其自动断开,断开时间基于配置的 wait_timeout
参数,默认值是8小时。安全
若是链接断开以后,客户端再次发送请求的话,就会收到错误:Lost connection to MySQL server during query
。此时,继续执行请求就须要重连。架构
MySQL中,长链接是指链接成功后,若是客户端持续有请求,则一直使用同一个链接。短链接则是指每次执行完很小的几回查询就断开链接,下次查询重建新链接。函数
创建链接的过程通常是比较复杂的,因此咱们应该尽可能使用长链接。
可是并非说长链接就没有问题,由于MySQL执行过程当中临时使用的内存是管理在链接对象中的,因此每次执行完请求,都会增长链接的内存占用,这些资源一直不释放会致使内存爆满,被系统强行kill掉,从现象上看就是MySQL异常重启了。工具
那么解决这个问题的方案就须要考虑如下两点:
一、按期断开长链接,或在程序中判断某个链接内存占用过大,就将其关闭,以后使用再链接便可。
二、MySQL 5.7以后的版本,有一个 mysql_reset_connection 的函数能够从新初始化链接资源,也就是释放该链接占用的内存。这个过程不须要重连和从新作权限验证就会将链接恢复到刚刚建立完时的状态。
链接创建完成以后,咱们就能够执行 select 语句了,此时就会来到执行逻辑的第二步:查询缓存。
这一步不作过多说明,由于MySQL 8.0版本直接将查询缓存的整块功能删掉了。
查询缓存咱们每每不须要使用,由于其命中率实在是低下。对一个表就行更新操做就会使表上的全部查询缓存清空。
分析器的执行逻辑就是,内建解析树,对其进行词法语法检查,生成新的解析树,语义检查(字段,表是否存在)等。
首先进行词法分析:主要是根据MySQL的关键字进行验证和解析以及识别表名和列名。
mysql> elect * from t where ID=1; ERROR 1064 (42000): 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 * from t where ID=1' at line 1
以后进行语法分析:在词法分析的基础上,判断sql语句是否合法。
若语句不正确(如字段不存在),就会收到报错信息:
若是咱们执行这个语句:其中表T中字段k不存在,则会在分析器阶段就报错
select * from T where k=1 Unknown column ‘k’ in ‘where c...
以后会进行一个权限检查,这也就是若是用户没有T表的权限,且sql中字段k页不存在,却报错为无权限的缘由。
通过分析器以后,MySQL已经知道咱们要作什么了,在开始执行sql以前,还要通过优化器的处理。
简单来讲,优化器就是优化sql执行计划。
优化器是在表里面有多个索引的时候,决定使用哪一个索引;或者在一个语句有多表关联的时候(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。
以上两种执行方法的逻辑结果是同样的,可是执行效率可能存在不一样,优化器的做用就是决定选择使用哪种方案。
优化器执行完以后,就会进入执行器阶段。
预埋问题:优化器怎么选择索引?会不会选错?
MySQL经过分析器知道了咱们要作什么,经过优化器知道了该怎么去作,到了执行器阶段,就开始执行逻辑了。
简单来讲,执行器就是检查权限,打开表,处理数据。
执行sql时,会判断sql中的表以及操做涉及到的其余表(如更新一张表触发更新另外一个表的触发器操做)在当前链接上有没有相关权限,若没有,则会返回权限错误;如有则继续处理数据
注:并不是在执行器才执行权限检查,事实上,在命中查询缓存时,会在缓存返回结果时,作权限验证。且在优化器以前也会调用
precheck
验证权限。
mysql> select * from T where ID=10; ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如 select * from T where ID=10;
这条语句,表T中的ID字段没有索引,那么执行的流程就是:
一、调用 InnoDB 引擎接口取表的第一行,判断ID值是否为10,不是则跳过,是则将该行信息存在结果集中。
二、调引擎接口继续获取下一行,重复第1步判断,直至最后一行数据。
三、执行器将结果集返回给客户端。
对于有索引的表,第一次调用的是引擎中取知足条件的第一行的接口,以后循环取知足条件的下一行。这些接口都是引擎中已经定义好的。
以后咱们能够在数据库的慢查询日志中看到一个 rows_examined
的字段,表示这个语句执行过程当中扫描了多少行。该值就是在执行器每次调用引擎获取数据行的时候累加的。
在某些场景下,执行器调用一次,在引擎内部则会扫描多行,所以引擎扫描行数和 rows_examined
并非彻底相同的。
在开启查询缓存时,此时的结果集会更新到查询缓存中。
一、在执行器阶段为何要判断对表是否具备执行查询的权限,而不是在分析器去判断?根据 MySQL 中的 information_schema 库的存储信息,理论上能够在分析器阶段判断是否有权限。
有时sql语句要操做的表不仅是sql字面上的那些,好比有个触发器,得在执行器阶段才能肯定,因此优化器前判断权限有时候不完整。
二、建立一个没有 select 权限的用户,执行 select * from T where k=1;
(表T中无k字段),报错信息为:select command denied
而不是:unknown column
是否能够说明在打开表以后才判断读取的列不存在?
并不能说明,为了安全起见,返回无权限信息确定是最好的
客户端经过链接器链接到服务端,获取到权限等信息,而后在链接的有效时长内(interactive_timeout 和 wait_timeout 参数控制,5.7版本会断开自动重连)对客户端请求进行处理。
判断sql是select/update/delete/insert 中的哪种,如果 select 判断查询缓存是否开启。(此时判断的select是简单判断,其余解析并无作,因此分析器会再次判断并解析)
若查询缓存开启,则
若命中,则在数据返回时会判断查询权限,权限经过则返回数据。
若未命中,则进入分析器。
若未开启直接进入分析器。
分析器进行词法分析,语法分析,检查sql的语法顺序等生成解析树,而后预处理器对解析树进一步分析,验证对表是否具备相应权限以及数据表,字段是否存在,验证经过后更新解析树,交给优化器处理。
优化器对sql的执行计划进行最优选择,将最优执行计划交给执行器。
执行器再次验证相应权限,验证经过后,调用引擎接口获取数据并将结果返回给客户端,若查询缓存开启,则更新缓存。如果update/delete/insert请求,则删除查询缓存。
一些容易漏掉的知识:
一、链接器是从权限表里面查询用户权限并保存在一个变量里面以供查询缓存、分析器、执行器在检查权限的时候使用。
二、sql在执行的过程当中,可能会有触发器这种在运行时才能肯定是否有相应权限的操做,分析器工做结束后的 precheck
操做是不能对这种运行时涉及到的表进行权限校验的,因此须要在执行器阶段进行权限检查。另外也是由于有precheck这个步骤,才会在报错时显示为用户无权,而不是字段k不存在,此举是为了避免向用户暴露表结构。三、词法分析阶段是从information_schema里面得到表的结构信息的。四、可以使用链接池的方式,将短链接变为长链接五、mysql_reset_connection 是MySQL为各个语言提供的api,非sql语句。六、wait_timeout 是非交互式链接的空闲超时,interactive_timeout 是交互式链接的空闲超时。执行时间不计入空闲时间。经过客户端链接的为交互式,经过程序链接的是非交互式