mysql查询优化器的错误执行和优化处理类型

​ 一条查询有很多种执行方式,最后都返回相同的结果.优化器的作用就是找到这其中最好的执行计划.

​ MySQL使用基于成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中最小的一个.

​ 通过查询当前会话的Last_query_cost 的值来得知MySQL计算的当前查询成本

show status like 'Last_query_cost';

一.导致优化器选择错误的执行计划的原因

1.统计信息不准确

​ MySQL 依赖存储引擎提供的统计信息来评估成本,但是有的存储引擎提供的信息是准确的,有的偏差可能非常大

2.执行计划中的成功估算不等同于实际执行的成本

​ 即使统计信息精准,优化器给出的执行计划也可能不是最优的;有时候某个执行计划虽然需要读取更多的页面,但它的成本却更小(比如这些页面都是顺序读或者这些页面再内存中)

3.MySQL是基于成本模型选择最优的计划

​ 我们可能希望执行的时间尽可能的短,MySQL 是选择基于成本模型选择最优的计划,但是MySQL选择的这些并不是最快的执行方式

4.MySQL不考虑其他的并发执行的查询

​ 并发执行的计划会影响到当前查询的速度

5.MySQL 不是任何时候都是基于成本的优化

​ 有时候基于规则优化,如果存在全本搜索的MATCH()子句,则存在全文索引的时候就使用全文索引,即使使用where和别的索引快,mysql 也会继续使用对应的全文索引

6.MySQL 不会考虑其不受控制的操作的成本

​ 例如执行存储过程或者用户自定义函数的的成本

7.无法估算所有可能的执行计划

​ 这样导致mysql 可能错过实际上最后的执行计划

二.优化策略的分类

​ mysql 的查询优化是一个非常复杂的部件,它使用很多优化策略来生成一个最优的执行计划

​ mysql 对查询的静态优化只需要做一次,但对查询的动态优化则再每次查询的时候都重新评估,有时甚至在查询的执行过程中也会重新优化

​ 1.静态优化(编译时优化)

​ 静态优化可以直接对解析树进行分析,并且完成优化

​ 优化器可以通过一些简单的代数变换将where 条件转换成另一种等价形式,静态优化不依赖于特别的数值,如where 条件中带入的一些常数等.静态优化再第一次完成后就一直有效,即使使用不同的参数重复执行查询也不会发生变化

​ 2.动态优化(运行时优化)

​ 和查询的上下文有关,也可能和其他很多因素有关

​ 例如,where 条件中的的取值,索引中条目的对应的数据行,这需要再每次查询的时候重新评估

三.MySQL能处理的优化类型

​ 1.重新定义关联表的的顺序

​ 数据表的关联并不是总是按照在查询中指定的顺序进行,

​ 2.将外连接转化成内连接

​ 并不是所有的 outer join 语句都必须以外连接的方式执行

​ 3.使用等价变换规则

​ mysql 可以使用一些等价变换来简化并规范表达式.它可以合并并减少一些比较,还可以移除一些恒成立和一些恒不成立的判断

​ 4.优化count(),min() 和max()

​ 索引和列是否可为空通常可以帮助MySQL优化这类表达式.

​ 要找到某一列的最小值,需查询对应B-Tree 索引最左端的记录, 在B-Tree 索引中,优化器会将这个表达式作为一个常数对待,同样,要查找一个最大值,只需要读取B-Tree 索引的最后一条记录

​ 没有任何where条件的count(*)查询通常也可以使用存储引擎提供的一些优化

​ 5.预估并转化为常数表达式

​ 当mysql 检测到一个表达式可以转化为常数的时候,就会一直把该表达式作为常数进行优化处理

​ 一个用户自定义变量在查询中没有发生变化就可以转换一个常数,在优化阶段,有时候甚至一个查询也能转化为一个常数,比如执行min()函数,甚至主键或者唯一键查找语句也可以转换为常数表达式

​ 如果where 字句中使用了该类索引的常数条件,mysql可以在查询开始阶段就开始查找这些值,这样优化器就能够知道并转换为常数表达式

​ 6.覆盖索引扫描

​ 当索引中的列包含所有查询中需要使用的列的时候,mysql就可以使用索引返回需要的数据,而无需查询对应的数据行

​ 7.提前终止查询

​ 在发现已经满足查询需求的时候,mysql总是能够立刻终止查询

​ 一个简单的例子就是使用了LIMIT子句的时候,例如当发现了一个不成立的条件,这时候立刻返回一个空结果

​ 类似这种"不同值/不存在"的优化一般可用于DISTINCE,NOT EXIST() 或者left join 类型的查询

​ 8.等值传播

​ 如果两个列的值通过等式关联,那么mysql能够把启动一个列的where条件传递到另外一列上

​ 9.列表IN()的比较

​ 在很多数据库中,in()完全等同于多个or条件的子句,但是在mysql 中这点是不成立的, mysql将in()列表中的数据先进行排序,然后通过二分查找的方法来确定列表中的值是否满足条件,对于in()列表中有大量取值的时候,mysql 的处理速度将会更快

​ 如果能够确定优化器给出的不是最佳选择,并且清楚背后的原理,那么可以帮助优化器做进一步的优化,否则有可能使查询变得更加复杂而难以维护,而最终收益为0,让优化器按照他的方式工作就好了

发布了22 篇原创文章 · 获赞 5 · 访问量 1052

猜你喜欢

转载自blog.csdn.net/lighter613/article/details/103459556