MySQL在我们的开发中基本每天都要面对。MySQL作为开发中的数据源,承担着存储数据和读写数据的责任。因为学习和理解MySQL非常重要,那么我们在客户端发起一条SQL,直到出现详细的查询数据,究竟经历了怎样的一个过程呢?MySQL服务器是如何处理请求并执行SQL语句的?本博客将在未来探讨这些问题。MySQL执行流程MySQL的整体执行流程如下图所示:时刻,只有客户端可以与客户端通信。服务器请求或服务器向客户端发送数据,但不是同时,MySQL与客户端TC/IP连接。2.验证请求用户的帐号和密码是否正确。如果账号和密码不正确,会报错:Accessdeniedforuser'root'@'localhost'(usingpassword:YES)3.如果用户的账号和密码通过验证,系统会查询该用户的权限MySQL自带的权限表中的当前用户。MySQL中控制权限的表有4张,分别是user表、db表、tables_priv表和columns_priv表:instancedb表:存储数据库级别的权限,决定哪些用户从哪些主机可以访问这个数据库tables_priv表:存储表级别的权限,决定哪些用户从哪些主机可以访问数据库的这张表权限决定了哪些用户从哪些主机可以访问数据库表的这个字段。MySQL权限表的验证过程如下:1.首先从用户表中的Host、User、Password三个字段判断连接IP,用户名,密码是否存在,存在则验证通过.2.身份认证通过后,按照user、db、tables_priv、columns_priv的顺序进行权限分配和验证。即先查看全局权限表user,如果user中对应权限为Y,则该用户对所有数据库的权限为Y,不再查看db、tables_priv、columns_priv;如果是N,在db表中查看这个用户对应的具体数据库,在db中获取权限Y;ifNindb,查看tables_priv中这个数据库对应的具体表,获取表中的权限Y,以此类推。3.任何一个流程权限校验不通过都会报错。缓存MySQL的缓存的主要作用是提高查询效率。缓存以键和值的哈希表的形式存储。键是具体的SQL语句,值是结果的集合。如果无法命中缓存,则继续分析器步骤,命中缓存则直接返回客户端。不过需要注意的是,在MySQL8.0版本之后,缓存已经被官方删除了。之所以删除它是因为查询缓存失效非常频繁。如果在写多读少的环境下,缓存会频繁的添加和失效。对于一些更新压力大的数据库,查询缓存的命中率会很低。为了维护缓存,MySQL可能存在一定的可扩展性问题。目前,它在5.6版本中已默认禁用。值得推荐。方法是把缓存放在客户端,性能会提升5倍左右。分析器分析器的主要功能是对客户端发送过来的SQL语句进行分析,其中会包括预处理和解析过程。这个阶段会解析SQL语句的语义,提取关键字和非关键字进行解析。,并形成一个解析树。具体关键字包括但不限于:select/update/delete/or/in/where/groupby/having/count/limit等,如果检测到语法错误,直接向客户端抛出异常:“错误:您的SQL语法有错误。”。例如:select*fromuserwhereuserId=1234;在分析器中通过语义标尺提取并匹配关键字selectfromwhere,MySQL会自动判断关键字和非关键字,将用户的匹配字段与self定义语句进行识别。这个阶段还会做一些检查:比如检查当前数据库中是否存在user表,如果user表中不存在userId字段,也会报错:“unknowncolumninfield列表。”。优化器可以进入优化器阶段,表明SQL符合MySQL的标准语义规则,可以执行。这个阶段主要是对SQL语句进行优化,会根据执行计划进行最优选择,匹配合适的索引,选出最优秀的执行计划。比如一个典型的例子是这样的:表T,在A、B、C列上建立联合索引,查询时,SQL查询的结果是:selectxxwhereB=xandA=xandC=x,很多人认为索引没有用到,其实是会用到的。虽然索引必须符合最左原则才能使用,但本质上,优化器会自动将这条SQL优化为:whereA=xandB=xandC=X,这个优化会匹配底层的索引,并且这个阶段会根据执行计划自动进行预处理。MySQL会计算出每一种执行方式的最佳时机,最终确定一条执行的SQL交付给最终的执行者。执行者处于执行者阶段。这个时候会调用存储引擎的API,API调用存储引擎。主要有存储引擎,不过常用的有myisam和innodb:引擎以前的名字是:tableprocessor(其实这个名字我觉得更能表达它存在的意义)它负责操作具体数据文件,分析select或update等SQL的语义,进行具体操作。执行后,具体的操作会记录在binlog中。需要注意的一点是:select不会记录在binlog中,只有update/delete/insert会记录在binlog中。更新会采用两阶段提交的方式,记录都在redolog中。可以通过命令显示执行状态:showfullprocesslist,显示所有的处理进程,主要包括以下状态,表示服务器处理客户端的状态。状态包括客户端发起请求到后台服务器处理的过程,包括被锁定的。处理、统计存储引擎信息、排序数据、查找中间表、发送数据等。它包含了所有MySQL的所有状态,具体含义如下:SQL执行顺序其实SQL并不是按照我们写的顺序从前到后,从左到右依次执行的,它是按照固定的顺序解析的对,主要作用就是返回上一阶段的执行结果,为下一阶段提供。SQL执行过程中,会出现不同的临时中间表,一般顺序如下:例:selectdistincts.idfromTtjoinSsont.id=s.idwheret.name="Yrion"group通过t.mobilehavingcount(*)>2orderbys.create_timelimit5;from第一步是选择fromkey后面的table这个词也是SQL执行的第一步:它表示从数据库中执行哪个表。示例说明:本示例首先从数据库中找到表T。joinonjoin是要关联的表,on是连接的条件。通过from和joinon选择需要执行的数据库表T和S,生成笛卡尔积,生成临时中间表Temp1,用于合并T和S。on:判断表的绑定关系,生成临时表中间表Temp2通过。示例说明:查找表S,生成临时中间表Temp1,然后找到表T与S的id相同的部分,组成表Temp2,其中包含所有T和Sid相等的数据。wherewhere表示过滤,根据where后面的条件过滤,根据指定字段的值从临时中间表Temp2中过滤出需要的数据(如果有and连接符,则进行联合过滤)。注意,如果这个阶段没有查到数据,会直接返回给客户端,不会再进行下一步。这个过程会生成一个临时中间表Temp3。注意:where不能使用聚合函数,聚合函数主要是(min\max\count\sum等函数)。示例说明:在temp2临时表集合中查找T表的name="Yrion"的数据,查找到数据后将成为临时中间表Temp3,T。emp3包含name列为“Yrion”的所有表数据groupbygroupby是将where条件过滤的临时表Temp3按照固定字段分组生成临时中间表Temp4。这个过程只是改变了数据的顺序,但数据总量不会改变,表中的数据是以组的形式存在的。示例说明:在Temp3表数据中对mobile进行分组,找出与mobile相同的数据,拼在一起生成Temp4临时表。Having聚合了临时中间表Temp4,可以进行count等统计,然后生成中间表Temp5,这个阶段可以使用select中的别名。示例说明:在Temp4临时表中查找编号大于2的数据。如果小于2,则直接丢弃,然后生成临时中间表Temp5。Select从已经分组聚合的表中选择要查询的数据。如果是*,则解析成所有数据,此时会生成一张中间表Temp6。示例说明:本阶段筛选Temp5临时聚合表中S表中的id生成Temp6。此时Temp6只包含了S表的id列数据,以及name="Yrion",通过mobile的组数大于2的数据。DistinctDistinct对所有数据进行去重。这时候如果有min和max函数,就会进行域函数计算,然后生成临时表Temp7。示例说明:该阶段对Temp5中的数据进行去重,引擎API会调用去重函数对数据进行过滤,最后只保留id第一次出现的数据,生成临时中间表Temp7。orderby会按照Temp7依次或倒序排列,然后插入到临时中间表Temp8中。这个过程消耗更多的资源。示例说明:本节将所有Temp7临时表中的数据按照创建时间(create_time)进行排序,这个过程中不会有列或行丢失。limitlimit对中间表Temp8进行分页,生成临时中间表Temp9,返回给客户端。示例说明:对Temp7中的数据进行排序,然后取前5个插入到临时表Temp9中,最后返回给客户端。PS:其实这个过程不完全是这样的。MySQL在中间会进行局部优化,以达到最好的优化效果,比如过滤掉select中找到的数据集。小结本篇博客总结了MySQL的执行过程和SQL的执行顺序。了解这些有助于我们优化SQL语句,了解SQL语句在MySQL中从编写到最终执行的轨迹,有助于我们对SQL的理解有更深入细致的理解,从而提高我们对数据库的理解能力。同时对于复杂SQL的执行过程和编写也会有一定的借鉴意义。
