MySQL:多列排序所需的索引

时间:2017-04-24 09:56:14

标签: mysql sql indexing

拥有一个包含三列的超过一千万个表:onetwothree和SQL查询,如SELECT * FROM table ORDER BY one, two, three LIMIT 1 - 我是否真的需要创建一个多列使用所有三列的索引?

我确信,如果onetwo匹配,则会有最多10行具有不同的three

快速SELECT是否足够? -

CREATE INDEX MY_INDEX ON table (one, two);

2 个答案:

答案 0 :(得分:1)

使用INDEX(one, two, three),查询将直接沿着BTree转到所需的一行(LIMIT 1)。

使用INDEX(one, two),查询将直接从BTree到第一行,然后向前扫描最多10行,将它们保存到tmp表中,对它们进行排序(ORDER BY包括three)(可能在内存中完成),并提供第一个。虽然这听起来更复杂但不会(在这个例子中)慢得多。

它不是“表扫描”(“全部”),但可能是“范围”扫描。使用EXPLAIN SELECT ...查看。

如果three是一个庞大的字符串,那么3-col索引会更笨重;这对磁盘空间和性能有一些影响。

如果您只需要(one, two)进行其他一些查询,那么 索引的效果相当不错(除非“笨重”的评论)。

如果你SELECT one, two, three FROM ...,那么3部分索引会更好,因为它会“覆盖”。 SELECT *不会有这样的奖励。

底线:任何一个指数都是“OK”,还有很多其他因素,因此很难确定该怎么做。

答案 1 :(得分:0)

您可能认为MySQL足够聪明,只能使用索引读取前10行,然后对它们进行排序。不幸的是,它不是(因为此时优化​​器不考虑limit)。您可以使用explain select ...验证,它将显示MySQL将执行全表扫描("ALL")。

documentation描述了能够使用索引优化order by的条件:

  

即使ORDER BY与索引不完全匹配,也可以使用索引,只要索引的所有未使用部分和所有额外的ORDER BY列都是WHERE子句中的常量。

您的第三栏不满足此要求。所以这个查询不会使用这个索引(这并不意味着它可能对其他东西没用)。

从MySQL 5.6开始,有一个所谓的filesort priority queue optimization来容纳limit:而MySQL仍然会读取整个表,它不会对整个表进行排序(这将是一个时间消费过程),但是当它知道第一行是什么时会停止,这使你的查询可以快速接受。

但是您可以重写您的查询以完全按照您的想法执行:

SELECT * FROM 
  (select * from table ORDER BY one, two LIMIT 10) sub
order by one, two, three limit 1;

这将使用该索引读取前10行,然后对它们进行排序。如果您绝对确定最多只有10行,它当然只能正常工作。

一种更通用的方法来优化查询,而不是知道最大可能行数,例如

SELECT * FROM table
where one = (select min(one) from table)
order by one, two, three limit 1;

这将使用索引通过首先查找one的最低值(使用索引)并仅考虑这些行来减少必须读取和排序的行数。您可以类似地包含two的条件。

或者你可以简单地使用索引中的所有三列(尽管取决于你的第三列的大小,不这样做是有意义的)。这种优化往往会在一个方面迎头赶上。如果你是使用第一种方法,并且在2年内可能会有11行,您(或您的继任者)必须记住您的代码中有这种隐含条件。