拥有一个包含三列的超过一千万个表:one
,two
,three
和SQL查询,如SELECT * FROM table ORDER BY one, two, three LIMIT 1
- 我是否真的需要创建一个多列使用所有三列的索引?
我确信,如果one
和two
匹配,则会有最多10行具有不同的three
。
快速SELECT是否足够? -
CREATE INDEX MY_INDEX ON table (one, two);
答案 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行,您(或您的继任者)必须记住您的代码中有这种隐含条件。