标签:
查询的生命周期的下一步是将一个sql转化成一个执行计划,MySQL再依照这个执行计划和存储引擎进行交互。这包括多个子阶段:解析sql,预处理,优化sql执行计划。这个过程中任何错误(例如语法错误)都可能终止查询。这里不打算详细介绍MySQL内部实现,而只是选择性的介绍其中几个独立的部分,在实际中,这几部分可能以前执行也可能单独执行。我们的目的是帮助大家理解MySQL是如何执行查询的,以便写出更优秀的查询。
· 语法解析器和预处理
首先,MySQL通过关键字语句进行解析,并生成一科对应的“解析树”,MySQL集线器将使用MySQL语法规则验证和解析查询。例如,它将验证是否使用错误的关键字,或者使用关键字的顺序是不是正确等,再或者他还会验证引号是否能前后正确匹配。
预处理器则根据一些MySQL规则进一步验证解析树是否合法,,例如,这里将坚持数据表和数据列是否存在,还会解析名字和别名,看看他们是否有歧义。
下一步预处理器会验证权限。这通常会非常快,除非服务器上有非常多的权限配置。
查询优化器
限制语法数被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种查询方式,最后都会返回相同的结果。游虎丘的作用就是找到这其中最好的执行计划。
MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。最初,成本的最小单位是随机读取一个4k的数据源的成本,后来(成本的计算公式)变得更加复杂,并且引入了一些“因子”来估算某些操作的代价,如当执行一次where条件比较的成本。可以通过查询当前会话的Last_query_cost的值来的值MySQL计算的当前查询的成本。
SELECT SQL_NO_CACHE COUNT(*) FROM actor;
SELECT STATUS LIKE ‘Last_query_cost‘
Variable_name Value
Last_query_cost 1040.343400
这个结果表示MySQL的优化器认为大概需要做1040个数据源的随机查找才能完成上面的查询。这是根据一系列的统计信息计算得来的:每个表或者索引的页面个数、索引的基数(索引中不同值的数量)、索引和数据行的长度、索引的分布情况。优化器在评估成本的时候并不会考虑任何层面的缓冲,它假设读取任何数据都需要一次磁盘io
很多原因会导致MySQL优化器选择错误的执行计划,如下所示:
统计信息不准确。MySQL依赖存储引擎提供的统计信息来评估成本,但是有的存储引擎提供的信息是准确的,优点偏差可能非常大。例如,innodb因为其mvcc的架构,并不能维护一个数据表的行数的精确的统计信息。
执行计划中的成本估算不等同于实际执行的成本。所以即使统计信息准确,优化器给出的执行计划也可能不是罪优的。例如有时候某个执行计划虽然需要读取更多的页面,但是他的成本却更小。因为如果这些页面都是顺序读取或者这些页面都已经在内存中存在,那么它的访问成本将会很小。MySQL层面并只知道那些页面在内存中,那些在磁盘上,所以查询实际执行的过冲中到底需要多少次窝里io是无法得知的。
MySQL的最优化可能和你像的最后不一样。你可能希望执行时间尽可能的短,但是MySQL只是基于其成本模型选择最优的执行计划,而有些时候这并不是最快的执行方式。索引,这里我们看到的根据执行成本来选择执行计划并不是完美的模型。
MySQL从不考虑其他并发的执行查询,这可能会影响到当前的查询速度。
MySQL也并不是任何时候都是基于成本的优化。有时也会基于一些固定的规则,例如,如果在全文所搜的MATCH()子句,则在全文索引的时候就使用全文索引,即使有时候使用别的索引和where条件可以远比这方式要快,MySQL仍然会使用对应的全文索引。
MySQL不会考虑其控制的操作成本,例如执行存储过程或者用户自定义函数的成本。
后面我们还会看到,优化器有时候无法去估算所有可能的执行计划,所以它可能错过实际上最优的执行计划。
MySQL的查询优化器是一个非常复杂的部件,它使用了很多优化策略来生成一个最优的执行计划。优化策略可以简单的分为两种:一种是静态优化,一种是动态优化。静态优化可以直接对解析树进行分析,并完成优化。例如,又好看可以通过一些简单的代数变化将WHERE 条件转换成另一种等价的形式。静态优化不依赖于特别的数值,如where条件中带入的一些常数等。静态优化在第一次完成后就一直有效,即使使用不同的参数执行查询也不会发生变化。可以认为这是一种“编译时优化”。
相反,动态优化则和查询的上下文有关,也可能和很多其他因素有关,例如WHERE 条件中的取值,索引中那个条目对应的数据行数等。这需要在每次查询的时候都重新评估,可以认为这是 “运行时优化”。
在执行语句和存储过程的时候,动态优化和静态优化的区别非常重要。MySQL对查询的静态优化只需要做一次,但对查询的动态优化规则在每次执行的时候都需要评估,有时候甚至在查询的过程中重新优化(例如,在关联操作过程中,范围检查的执行计划会针对每一行重新评估索引。可以通过EXPLAIN 执行计划红的Extra列是否有“range checked for each record” 来确认这一点。该执行计划还会增加select_full_range_join 这个服务器变量的值)。
下面是MySQL能够处理的优化类型:
重新定义关联表的顺序
数据表的关联并不总是按照在查询中指定的顺序进行。决定关联的顺序是优化器很重要的一部分功能。
将外连接转化成内连接
并不是所有的OUTER JOIN 语句都必须以外连接的方式执行。诸多因素,例如WHERE 条件,库表结构都可能会让外连接等价成一个内连接。MySQL能够识别这点,并重写查询,让其可以调整关联顺序。
使用等价变换规则
MySQL可以使用一些等价变换来简化并规范表达式。它可以合并和减少一些比较,还可以移除一些恒成立和一些恒不成立的判断。例如(5=5 and a>5) 将被改写成a>5 .类似的,如果有(a<b AND b<c) AND a = 5则会改写成b>5 AND b=c AND a=5。这些条件对于我们编写条件语句很有用。
优化COUNT () ,MIN()和MAX()
索引和列是否可为空通常可以帮助MySQL优化这类表达式。例如,要找到某一列的最小值,只需要查询在B-Tree索引最左短的记录,MySQL可以直接获取索引一行记录。在优化器生成执行计划的时候就可以利用这一点,在B-Tree 索引中,优化器会将这个表达式作为一个常数对待。类似的,如果要查找一个最大值,也只需要读取b-tree所用的最后一条记录。如果MySQL使用了这种类型的优化,那么再EXLAIN中就可以看到’Select tables optimized away‘ 。从字面的意思可以看出,他表示优化器已经冲执行计划中移除了该表,并以一个常数取而代之。
标签:
原文地址:http://www.cnblogs.com/zhengyanqiu/p/5011759.html