一条sql查询语句是如何执行的。
通过mysql的执行计划(explain) 探寻select 语句的神秘
explain select * from course_order where id > 10;
下图为执行后的结果
explain 中的列描述
id列
标识select 所属的行。如果语句汇当中没有子查询或联合,那么只会有唯一的select,于是每一行在这个列中都将显示一个1。否则,内存select语句一般会顺序编号,对应与其在原始语句汇总的位置。
select查询分简单和复杂类型
复杂类型分成三大类:简单子查询、所谓的派生表(在from子句中的子查询),以及union查询
三类语句的混合则会使输出变得非常复杂。
select_type 列
这一列显示了对应行是简单还是复杂select。simple值意味着查询不包括子查询和union。如果查询有任何复杂的子部分,则最外层部分标记为primary,其他部分标记如下:
subquery:包含在select列表中的子查询中的select标记为subquery
derived:用来标识包含在from子句的子查询中的select,mysql会递归执行并将结果放到一个临时表中。服务器内部称其“派生表”,因为该临时表是从子查询中派生来的。
union:在union中的第二个和随后的select被标记为union。第一个select被标记就好像它以部分外查询来执行。这就是之前的例子中在union中的第一个select显示为primary的原因。如果union被from子句中的子查询包含,那么它的第一个select会被标记derived。
union result:用来从union的匿名临时表检索结果的select被标记为union result
table列
显示对应行正在访问哪个表。是一个表,或者是标的别名。
可以在这一列中从上往下观察mysql的关联优化器为查询选择的关联顺序。
type列
显示了"关联类型",更准确的说法是访问类型-换言之就是Mysql决定如何查找表中的行。下面依次从最差到最优
ALL 全表扫描
index 跟全表扫描一样,只是扫描表时按索引次序进行而不是行。主要避免了排序。缺点是要承担按索引次序读取这个表的开销。
range 范围扫描就是一个有限制的索引扫描,它开始与索引里的某一点,返回匹配这个值域的行。这比全索引扫描好一些。扫描是带有between或在where子句中带有>的查询
ref 这是一种索引访问,它返回所有匹配某单个值的行。它是查找和扫描的混合体。只有当使用非唯一性索引或者唯一性索引的非唯一性前缀时才会发生。
eq_ref 使用这种索引查找,最多只返回一条符合条件的记录。以使用主键或者唯一索引查找时看到。
const,system 对查询的某些部分进行优化将其转换成一个常量时
null 意味着mysql能再优化阶段分解查询语句,执行阶段用不着访问表或者索引。
possible_keys 列
这一列显示了查询可以使用那些索引,这是基于查询访问的列和使用的比较操作符来判断。这个列表是在优化过程的早期创建的,因此有些罗列出来的索引可能对于后续优化过程是没用的。
key列
该列显示了Mysql决定采用哪个索引来优化对该表的访问。如果该索引没有出现在possible_keys列中,那么选用它是出于另外的原因,它可能选择了一个覆盖索引,哪怕没有where子句。
key显示的是优化采用了最小化查询成本的那个索引。
key_len 列
显示了在索引里使用的字节数。指的是此次索引匹配上的字节大小,非数据表中的对应数据大小。
ref列
这一列显示了之前的表在key列记录的索引查找值所用的列或常量。
rows列
估计为了找到所需的行而要读取的行数。这个数组是内嵌循环关联计划里的循环数目。估算的值,并不精准。
filtered列
在使用explain extended时出现(可能随着版本的演进,高版本默认出现了,我刚刚就出现了)显示的是针对表里符合某个条件的记录数的百分比所做的一个悲观估算。
Extra列
这一列包含的是不适合在其他列显示的额外信息。
using index 此值标识使用覆盖索引,以避免回表
using where 将在存储引擎检索行后再进行过滤。许多where条件里涉及索引中的列,当它读取索引时,就能被存储引擎检验,出现时可能表示查询可受益于不同的索引
using temporary 对查询结果排序时会使用一个临时表
using filesort 会对结果使用一个外部索引排序,而不是按索引次序从表里读取行
range checked for each recor (index map :N)以为着没有好用的索引,新的索引将链接的每一行上重新估算。
Java 编程规约推荐
大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。
不同的存储引擎公用一个Server层
连接器
mysql -hlocolhost -P3306 -uroot -p
show processlist -》 查看数据库连接列表,command列表示状态 对应的Sleep值意为空闲连接,太长时间没动静,自动断开,参数为wait_timeout管理,默认值为8小时。
查询缓存
MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。
系统配置表适合使用查询缓存,数据表为静态的,一旦表被更新,该表对应的缓存将全部失效,对于更新频繁的表来说,查询缓存毫无意义。
好在 MySQL 也提供了这种“按需使用”的方式。你可以将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存。而对于你确定要使用查询缓存的语句,可以用 SQL_CACHE 显式指定,像下面这个语句一样:
mysql>select SQL_CACHE *from T where ID=10;
需要注意的是,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。
分析器
进行词法分析语法分析,如果出现问题将会报错
优化器
到了这里,数据库已经知道要做什么了,就开始进行优化了。
优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。
开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限
打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。
调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;调用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。
以上就是mysql 中一条select 语句的执行流程了,大家加油!