mysql执行计划减少成本与订单?

时间:2017-09-15 10:37:23

标签: mysql sql

为什么mysql workbench会告诉我:

SELECT * FROM file_results WHERE filter2_dec > 20 LIMIT 3

的成本高于:

SELECT * FROM file_results WHERE filter2_dec > 20 ORDER BY filter2_dec DESC LIMIT 3

enter image description here

enter image description here

4 个答案:

答案 0 :(得分:1)

最有可能的原因是,在查询的顺序中,您已指定它为DESC。这更容易找到,因为数据已经订购。而在你的另一个声明中,它仍然必须遍历每个条目,然后限制在前3个。

由于您在该条款之后限制,因此订单减少。数据已经订购,因此更容易显示结果。而更昂贵的陈述必须自己做,然后限制。这也将受到索引DBMS的影响,并且在实际的实时环境中可能会有不同的表现。

答案 1 :(得分:1)

这与MySQL(当前)优化limit的方式有关。它基本上计算查询,就好像没有限制,然后对查询计划进行一些调整,以确认它可能有效。

这会产生副作用,你不能再真正相信成本价值 - 如果你可以信任它:它的绝对值没有多大意义,重要的是它低于所有其他执行计划

理论

首先,请注意,成本值并不考虑您获得的实际行数。你可以,例如增加限制值,它不会改变绝对成本(直到它切换到不同的执行计划)。

使用索引检索3行显然是一个不错的选择。这就是为什么两个查询都使用这种方式。但是使用(非覆盖)索引需要查找表以检索其他列的值(select *)。它将读取索引,然后从表中读取一行,然后从索引中读取下一个条目,然后从表中读取下一行。在某些时候,只需读取整个表(“全表扫描”)并丢弃不需要的行而不是使用索引就显得更快。

如果您必须订购数据,那么这些要点比您之后不必订购数据要晚。

如果您不使用limit,MySQL将通过查看filter2_dec > 20并猜测它将获得多少行来决定是否达到该点。尝试在没有limit的情况下执行查询。它将使用全表扫描(否则使用低于20的数字)。

现在将值20(仍然没有限制地)增加到足够高的值,以便只获得一些行(但大于0);我们假设该值为1000

现在棘手的部分:如果你添加一个limit,那个点(最好使用范围扫描而不是表扫描的值)显然会改变。

MySQL将尝试通过在生成的所有可能执行计划的列表中进行一些修改来包含该limit的效果,就好像没有限制一样。例如。扔掉其中一些或根据它们添加修改过的副本。这可能导致成本的奇怪值(因为它们实际上不是该操作的成本,而是原始计划的成本)。

练习

让我们看看这一点。因为它取决于确切的优化器和mysql版本,所以你的实际行为可能会有所不同。但对于这个简单的查询,它仍然应该保持一般。我将假设SELECT * FROM file_results WHERE filter2_dec > 20将使用全表扫描,而SELECT * FROM file_results WHERE filter2_dec > 1000将使用它自己的索引(并且实际上将返回一些行)。如果没有,请调整该值或添加更多行(并可能运行optimize table)。

首先,尝试以下两个查询:

SELECT * FROM file_results WHERE filter2_dec > 20 
order by filter2_dec 

SELECT * FROM file_results WHERE filter2_dec > 20 
order by filter2_dec limit 10

第一步应该显示相同的成本,但第一步将是“全表扫描”,而另一步则是“索引范围扫描”。

limit基本上采用原始计划并用“索引范围扫描”替换“全表扫描”,但无法计算新成本。 MySQL只是“知道”由于极限,全表扫描使用起来很荒谬,并将其更改为范围扫描。 “order by”本身不再需要任何费用。

现在检查以下3个查询:

SELECT * FROM file_results WHERE filter2_dec > 20 

SELECT * FROM file_results force index (file_results_filter2_dec_index)
WHERE filter2_dec > 20 

SELECT * FROM file_results WHERE filter2_dec > 20 limit 10

第二个和第三个应该有相同的成本,而第一个应该更便宜 - 但被limit - 操作抛出,所以第二个(和第三个)现在是最便宜的方式执行查询(你可以从字面上理解:limit强制你使用索引,因为其他一切都只是愚蠢的)。最后,这就是您的查询看起来比另一个查询更昂贵的原因。它基于不同的计划。

最后一次检查:

SELECT * FROM file_results WHERE filter2_dec > 1000 
order by filter2_dec

SELECT * FROM file_results WHERE filter2_dec > 1000 
order by filter2_dec limit 10

SELECT * FROM file_results WHERE filter2_dec > 1000 limit 10

应该都显示相同的执行计划和相同的成本(有或没有限制)。即使没有限制,该计划也将自行选择。 limit - 优化器完成的调整不需要这些计划,只需要开始时更加昂贵的计划。

答案 2 :(得分:0)

可能是cos filter2_dec得到的是结构列索引。但是,如果你需要一些优秀的分析师,我认为你必须通过关于该表的结构引擎使用......问候

答案 3 :(得分:0)

我猜想以下几点。

在第一个查询中,filter2_dec > 20不是很有选择性。所以,MySQL说:“我将选择很多行,所以我只会进行全表扫描”。 LIMIT可能会增强这一点,因为如果值随机存储在行中,它可能会很快达到3。根据{{​​1}}子句的选择性拒绝使用索引的选择。

在第二个查询中,WHERE保留了使用索引作为可能性的选项。如果引擎执行全表扫描,则需要对所有数据进行排序。相反,使用索引更有意义 - 最终可以节省成本。

换句话说,ORDER BY子句的选择性拒绝在考虑索引成本之前使用索引。在第二种情况下,由于WHERE,该选项仍处于打开状态。

这是猜测,但它可以解释你所看到的。优化者很难。