现在有联合索引(a,b),select* form tb where b =xx group by a执行流程是什么样子的?
CREATE TABLE IF NOT EXISTS `test`(
`id` INT(10) NOT NULL AUTO_INCREMENT COMMENT'主键',
`a` INT(10) NULL,
`b` INT(10) NULL,
PRIMARY KEY(id),
INDEX idx_a_b(a,b)
)ENGINE = INNODB;
INSERT INTO `test`(a,b) VALUES(2,3);
INSERT INTO `test`(a,b) VALUES(2,4);
INSERT INTO `test`(a,b) VALUES(2,5);
INSERT INTO `test`(a,b) VALUES(2,6);
INSERT INTO `test`(a,b) VALUES(2,7);
INSERT INTO `test`(a,b) VALUES(3,3);
INSERT INTO `test`(a,b) VALUES(3,4);
INSERT INTO `test`(a,b) VALUES(4,3);
INSERT INTO `test`(a,b) VALUES(4,4);
EXPLAIN SELECT * FROM `test` WHERE b = 3 GROUP BY a = 3;
首先是根据b查询,而a和b构建了联合索引,不满足最左匹配原则,不一定会走联合索引,如果查询优化器发现全表扫描的效率低于扫描联合索引的效率的话,就会走联合索引,但是因为不满足最左匹配原则,因此一定会把整个索引树都扫描一遍,取出b = xx的情况, 由于索引 (a, b)
已经按 a
排序,因此可以有效地使用索引顺序扫描来快速对 a
列进行分组。然后根据b取出对应的主键id,进行回表,拿到所有的数据后,创建一个临时的表来存储按a分组的数据,最终返回结果集。
如果查询优化器发现全表扫描的效率高于扫描联合索引的效率的话,就不会走索引,直接进行全表扫描,查询b=xx的记录,然后将所有记录按a进行分组存入临时表中,返回结果集。
explain执行计划:
执行流程总结(全表扫描索引的情况):
- 扫描联合索引
(a, b)
:MySQL 根据联合索引(a, b)
找到b = xx
的所有记录。 - 按
a
进行分组:由于联合索引(a, b)
中的a
已经排序,MySQL 可以直接根据a
进行GROUP BY
操作。 - 是否需要回表:
-
- 如果查询的列全部包含在联合索引中(如
SELECT a, b
),则不需要回表。 - 如果查询需要访问其他列(如
SELECT *
),则 MySQL 需要回表,从表中读取完整数据。
- 如果查询的列全部包含在联合索引中(如
- 返回结果集:将查询结果返回给客户端。
执行流程总结(全表扫描原数据表的情况):
- 全表扫描:
-
- MySQL 扫描原始数据表,逐行检查
b
列是否满足b = xx
的条件。 - 将符合
b = xx
的记录提取出来。
- MySQL 扫描原始数据表,逐行检查
- 放入临时表:
-
- 将符合条件的记录放入临时表中(如果数据量较大且无法在内存中处理时)。
- 根据
a
进行分组:
-
- 在临时表中对
a
列进行GROUP BY
操作,按a
分组。
- 在临时表中对
- 返回结果:
-
- MySQL 将分组后的结果返回给用户。