查看查询成本
查询上一次SQL的成本,单位页
SHOW STATUS LIKE 'Last_query_cost'
上述结果表示 MySQL 的优化器认为大概需要 12个数据页的随机查找才能完成上述的查询。
优化器做了什么?
语法树被认为合法时,优化器会将其转化成执行计划。一条查询可以有很多种执行方式,最后都返回相同结果。优化器的作用就是找到这其中最好的执行计划。MySQL 使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。
可能会导致优化器选择了错误的执行计划?
【1】统计信息不准确:MySQL 依赖存储引擎提供的统计信息来评估成本,但有的偏差可能非常大。例如,InnoDB 因为其 MVCC 的架构,并不能维护一个数据表的行数的精确统计信息。
【2】执行计划中的成本估算不等同实际执行的成本:所以即使统计信息精准,优化器给出的执行计划也可能不是最优的。例如某个执行计划虽然需要读取更多的页面,但是它的成本却更小。因为如果这些页面都是顺序读或者页面都已经在内存中的话,那么它的访问成本将很小。MySQL 层面并不知道哪些页面在内存中、哪些在磁盘上,所以查询实际执行过程中到底需要多少次物理 I/O 是无法得知的。
【3】MySQL 的最优可能和我们想的最优不一样:我们希望执行时间尽可能短,但是MySQL 只是基于其成本模型选择最优的执行计划,有时候并不是最快的执行方式。所以,我们根据执行成本选择执行计划并不是完美的模型。
【4】MySQL 从不考虑其他并发执行的查询:可能会影响到当前查询的速度。
【5】MySQL 也并不是任何时候都是基于成本的优化:有时也基于一些固定的规则。
【6】MySQL 不会考虑不受其控制的操作的成本:例如执行存储过程或者用户自定义函数的成本。
【7】优化器有时候无法估算所有可能的执行计划,所以它可能错过实际上最优的执行计划。 MySQL 的查询优化器使用了很多优化策略来生成一个最优的执行计划。优化侧率可以简单分为两种:静态优化和动态优化。静态优化可以直接对解析树进行分析,并完成优化。动态优化则和查询的上下文有关,也可能和很多其它因素有关,例如WHERE 条件中的取值、索引中条目对应的数据行数等。这需要在每次查询的时候都重新评估,可以认为这是 “运行时优化”。
下面是一些 MySQL 能够处理的优化类型:
【1】重新定义关联表的顺序:数据表的关联并不总是按照在查询中指定的顺序进行。决定关联的顺序是优化器很重要的一部分功能。
【2】使用等价变换规则:MySQL 可以使用一些等价变换来简化并规范表达式。它可以合并和减少一些比较,还可以移除一些恒成立和一些恒不成立的判断。例如:(5=5 AND a>5)将被改写为 a>5;
【3】将外连接转化成内连接:并不是所有的 OUTER JOIN 语句都必须以外连接的方式执行。
【4】优化 COUNT()、MIN() 和 MAX():要找到某一列的最小值,只需要查询对应 B-Tree 索引最左端的记录,MySQL 可以直接获取索引的第一行记录。在优化器生成执行计划的时候就可以利用这一点,在 B-Tree 索引中,优化器会将这个表达式作为一个常数对待。
【5】预估并转化为常数表达式:当 MySQL 检测到一个表达式可以转化为常数的时候,就会一直把该表达式作为常数进行优化处理。
【6】覆盖索引扫描:当索引中的列包含所有查询的列时,MySQL 就可以使用索引返回需要的数据,而无须查询对应的数据行。
【7】子查询优化:MySQL 在某些情况下可以将子查询转换一种效率更高的形式,从而减少多个查询多次对数据进行访问。
【8】提前终止查询:如果发现已经满足查询需求,MySQL 总是能够立刻终止查询。典型的例子就是 LIMIT 子句。
【9】列表IN() 的比较:很多数据库系统中,IN()完全等同于多个 OR 条件子句,因为这两个是完全等价的。在MySQL中不成立,IN()列表中的数据先排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个 O(logn)复杂度的操作,等价地转化成 OR 查询的复杂度为 O(n),对于 IN() 列表有大量取值的时候,MySQL 的处理速度将会更快。
数据库会根据执行计划,调存储引擎的 API 来执行查询
优化器:将语法树转化成执行计划。一条查询可以由多种执行方式,最终都返回相同的结果。优化器的作用就是找到其中最好的执行计划。MySQL 基于成本的优化器,它尝试预测一个查询使用某种执行计划的成本,并选择成本最小的一个。可以通过 SHOW STATUS LIKE 'Last_query_cost' 值得知 MySQL 计算的当前查询的成本。