我写了有关MySQL的loose index scan的相关博文(),后来我发现上次提到的那个优化方法中主要的目的就是实现loose index scan,而在执行计划的层面上看,Extra信息中应该是“Using index for group-by”。这样看来,可能MySQL在处理distinct时和group by用了同样的优化手段,即走索引,进行loose index scan。那么今天我研究了一下官方文档,发现确实如此。
其实对于group by来讲,最一般的实现方法就是进行一次全表扫描,将所有的group by的行按照顺序存放在一个temporary table中,然后在进行分组识别或者进行聚合操作。这样问题就是太复杂,时间上要好久,空间上的消耗也不小。这时,MySQL可以利用索引来优化group by。
这里就可以讲讲什么叫做loose index scan了,根据官方的定义,这种方法只需要扫描索引中的少部分数据,而不是所有满足where条件的数据,所以这个方法叫做loose index scan。
下面是什么情况下可以使用loose index scan的情况:
1 单一表查询
2 Group by中只有最左前缀列,没有其他列
3 只支持max和min聚合,而且,要聚合的列必须是group by中列所在的索引。
4 未被group by引用的索引其他部分必须是常量(这句我不是很理解)
5 不支持前缀索引。
假设t1(c1, c2, c3, c4)表有一个索引包括c1, c2, c3列,以下这些查询都是可以进行loose index scan的:
SELECT c1, c2 FROM t1 GROUP BY c1, c2;
SELECT DISTINCT c1, c2 FROM t1;
SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;
SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;
SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;
这些都是抄的官方文档,正确性未完全验证。
下面这些SQL都没有办法使用loose index scan:
-- 因为聚合函数不是max或者min
SELECT c1, SUM(c2) FROM t1 GROUP BY c1;
-- 因为不符合最左前缀原则
SELECT c1, c2 FROM t1 GROUP BY c2, c3;
-- 查询涉及到了索引的一部分,紧跟group by中的列,但是没有常量等值语句,加上 WHERE c3 = const就好了
SELECT c1, c3 FROM t1 GROUP BY c1, c2;
另外一些聚合函数也是可以用到loose index scan的,比如:AVG(DISTINCT), SUM(DISTINCT), 和COUNT(DISTINCT)
以下这些语句也可以:
SELECT COUNT(DISTINCT c1), SUM(DISTINCT c1) FROM t1;
SELECT COUNT(DISTINCT c1, c2), COUNT(DISTINCT c2, c1) FROM t1;
很多语句都是从5.7的文档上摘抄下来的,正确性没有得到验证,虽说官方文档是权威,但是尽信书不如无书,以及纸上得来终觉浅,绝知此事须躬行,明天周末,我逐条测试,然后再更。