2017-09-15 77 views
0

为什么MySQL工作台告诉我说:mysql的执行计划成本降低,订单由?

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

+0

这是根据实际运行时间还是估算成本计算的? –

回答

1

它很可能是因为通过查询你的订单已指定它为DESC。这很容易找到,因为数据已经排序。而在你的其他说法,它仍然有通过每一个要运行的条目,然后限制为前3名。

顺序由是减少,因为你该条款后限制。数据已经排序,因此更容易显示结果。而更昂贵的声明必须自己做,然后限制。这也将受到索引,数据库管理系统的影响,并可能在实际的现场环境中表现不同。

+0

当我删除desc时,我得到的order by语句的成本相同。 – Chris

0

可能是cos filter2_dec got on是结构列的索引。但是,如果你需要一些好的分析师,我认为你必须过去有关表结构利用发动机...关心

0

我猜以下。

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

在第二查询中,ORDER BY保持使用所述索引作为可能性的选择。如果引擎执行全表扫描,则需要对所有数据进行排序。相反,使用索引更有意义 - 最终会成为整体节省。

换句话说,所述WHERE子句的选择性拒绝指数被认为是成本之前使用索引的。在第二种情况下,该选项因ORDER BY而保持打开状态。

这是猜测,但它会解释你所看到的。优化器很难。

+0

执行计划表示,它使用索引范围扫描来扫描两者,而不是全扫描表:( – Chris

+0

@Chris ...。您会注意到'ORDER BY'在第二个计划中没有成本。在两个查询中,范围扫描的估计值是不同的。 –

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只是“知道”,由于限制,全表扫描使用起来会很荒谬,并将其改为范围扫描。 “顺序”本身不再花费任何东西。

现在检查以下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 

第2和第3应该具有相同的成本,而第一个要便宜一些 - 但得到了由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 -optimizer所做的调整不需要这些计划,只需要开始时更昂贵的计划。