【1】Mysql学习笔记:Mysql的一条查询语句是如何执行的?

在这里,我们以
select * from table where id = 10
为例子,进行查询。

Mysql基本架构示意图:
客户端+ server层+存储引擎层:
【1】Mysql学习笔记:Mysql的一条查询语句是如何执行的?
Server层:包括连接器、查询缓存、分析器、优化器、执行器等,涵盖Mysql的大多数核心服务功能,以及所有的内置函数(比如:日期、时间、数学和加密函数等),所有跨存储引擎的功能,都在这一层实现,比如存储过程、触发器、师徒等。

存储引擎层负责数据的存储和提取,其架构模式是插件式、支持InnoDB、MyISAM、Memory等多个存储引擎。
mysql的存储引擎默认的是InnoDB。执行creatable table语句的时候,如果没有指定存储引擎的类型,默认选取的就是InnoDB、但是也可以通过指定存储引擎的类型,来选择存储引擎,创建表。

连接器:
第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。
连接命令中,mysql是客户端的工具,用来跟服务端简历连接。在完成经典的TCP握手后,连接器,就要开始认证你的身份,这个时候,这个时候用的就是你输入的用户名和密码。
如果用户名或者密码不对,你就会收到一个access denied for user的错误,然后客户端程序,结束执行。
如果用户名和密码认证通过,连接器就会到权限表里检查出,你拥有的权限,之后,这个链接里面的权限逻辑判断,都将依赖于此时读到的权限。
这就意味着,一个用户成功建立连接后,即使你用管理员账号,对这个用户的权限做了修改,也不会影响到已经存在的连接。修改完成后,只有再新建连接时才会使用新的权限设置。
连接完成后,如果你没有后续的动作,这个连接,就处于空闲状态,你可以在showprocesslist命令中看到它,文本中这个图是show processlist的结果,其中的command列显示为sleep的这一行,就表示现在系统里面有一个空闲连接。

如果连接被切断之后,客户端再次发送请求的话,就会收到一个错误提醒:lost connection to mysql server during query。这个时候如果你要继续,就需要重新连接,然后在执行请求了。

数据库里面,长连接,是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接,则是值每次执行完成很少的几次查询,就断开连接,下次重新建立一个新的连接。

建立连接的动作比较复杂,耗时,因此在使用中,尽量减少连接的动作,也就是尽量使用长连接。
但是全部使用长连接以后,你可能会发现,有些时候mysql占用内存涨的特别快,这是因为Mysql 在执行的过程中,临时使用的内存是关联在连接对象里面的。这些资源会在连接断开的时候,才能释放。所以若果昌连接累计下来,可能导致内存占用太大,被系统强行杀掉,从现象来看,就是Mysql重启了

怎么解决这个问题,可以考虑以下两种方案。

[1]定时断开长连接。使用一段时间后,或者程序里判断,执行过一个占用内存的大查询之后,断开连接,之后查询重新建立连接。
[2] 如果使用的是Mysql 5.7或者更新的版本。可以再每次执行一个比较大的操作后,通过执行 mysql_reset_connection 将连接的资源初始化。连接会被恢复到刚刚创建时的状态。注意:这个过程不需要重新连接或者重新做权限验证.

查询缓存:

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

Mysql收到一个查询请求后,会先到 缓存区查看,之前是不是执行过这这句话。**之前执行过的语句以及其结果,可能会议key-value的对的形势,被直接缓存在内存中。key是查询的语句,value是查询的结果。**如果你的查询能够直接在这个缓存中找到key.那么这个value就会被直接返回个客户端。

如果,语句不在查询缓存中,就会继续后面的执行阶段。执行完成之后,执行接货会被存入查询缓存中。你可以看到,如果查询命中缓存。Mysql不需要执行就免的复杂操作,就可以执行效率很高。

但是大多数情况下,建不要使用查询缓存,为什么?因为查询缓存往往弊大于利。

查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存,都会被清空。 因此和可能,你费劲的把结构保存起来,还没有使用呢,就被一个更新全部清空了。对于更新压力大的数据库来说,查询缓存命中的概率,会非常的低。除非你的业务,就是有一张静态表,很长时间才会更新一次。比如一个系统配置表。这张表适合使用查询缓存。

需要注意的是,查询缓存,整块功能,全部删掉了,也就是说8.0开始,彻底没有这个功能了。

分析器
如果命中缓存,没有成功,就要开始真正的执行查询语句了。首先Mysql需要知道你要做什么?因此需要对sql语句做解析。
分析器先回做“词法分析”。你输入的是由多个字符串和空格,组合而成的一个sql语句。数据库需要识别出字符串分别是什么,分别代表什么。
mysql从你输入的“select”这个关键字,识别出来,这是一个查询语句。要把字符串 “table”识别成 “表明table”。把字符串ID,识别成列ID。

昨晚这些判断以后,就要做"语法分析"。词法分析器,会根据语法规则,判断,你输入的这sql语句是否满足mysql的语法。如果你的语句不对,就会收到提示: you have an error in your sql syntax。提示的内容,为第一个出错的地方。

优化器:

经过了分析器,mysql知道你要做什么了。在开始执行之前,还要先经过优化器的处理。优化器是表里面有多个索引的时候,决定各个表的连接顺序

比如: select * from table1 join table2 using(ID) where table1.c = 100 and table2.d = 300;

先可以从表1中取出 c列的取值为10的记录,在根据ID值,关联到表table2。再判断table2 里面 d的值,是否等于20。

也可以先从表 table2取出d = 300的记录的id值。再根据ID值关联到t1,再判断t1里面的c值,是否等于10。
这两种执行方法的逻辑结果是一样的。但是执行的效率会有不同,而优化器的左右就是决定选择使用哪一个方案。

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

执行器

执行器在执行之前,会先判断一下你对这个表table有没有执行查询的权限,如果没哟,就返回没有权限的错误
如下所示(在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。)

如果有权限,就打开表,继续执行。打开表的时候,执行求就会根据表的引擎定义。去使用引擎提供的接口。比如我们这个例子中的表Table1中,ID字段,没有索引。那么执行器的流程是这样的:
[1]调用Innodb引擎接口取这个表的第一行,判断ID值,是不是10.如果不是则跳过,如果是,则讲这行的运行结果。存在结果集中。
[2]调用引擎接口取 下一行 重复相同的判断扩及,知道渠道这个表的最后一行。
[3]执行器将上述遍历过程中所有满足条件的行,组成的记录集作为结果集返回给客户端。

至此,这个语句就执行完了。

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

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

在很多场景下。执行器调用一次。在引擎内初,扫描了很多行。因此引擎扫描行数跟rows_examindex并不是完全相同的。我们后面,会专门有一篇文章,来讲存储引擎的内部机制,里面会有详细的说明。

小结

一条sql语句执行的时候,会先去建立连接查询缓存分析器优化器、**“执行器”**五个阶段。
建立连接完成,在换缓存查询不到信息,就会真正的执行mysql语句,经过,分析器,优化器,执行器,将最终的结果返回去。