【GreatSQL优化器-18】GROUP_INDEX_SKIP_SCAN
一、GROUP_INDEX_SKIP_SCAN介绍
GreatSQL 优化器的分组索引跳跃扫描(GROUP Index Skip Scan) 是一种优化查询的技术,尤其在联合索引中用于减少扫描的无效行数。group by操作在没有合适的索引可用的时候,通常先扫描整个表提取数据并创建一个临时表,然后按照 group by 指定的列进行排序。在这个临时表里面,对于每一个group的数据行来说是连续在一起的。完成排序之后,就可以发现所有的groups,并可以执行聚集函数(aggregate function)。可以看到,在没有使用索引的时候,需要创建临时表和排序。在执行计划中通常可以看到“Using temporary; Using filesort”
GROUP索引跳跃扫描利用的是联合索引中非首列(非最左前缀)的索引列,来提高查询效率。例如,假如有个查询需求,需要查某个列的 distinct 值,或者 group by 之后的值 MIN()/MAX() 值,最简单的方式是扫描整个数据页,然后分组排序后,取 DISTINCT/MIN/MAX 值,但由于索引本身就有序并且完成了 group by 工作,如果可以直接借助于这个索引的有序性,那么扫描整个索引就可以避免二次排序的开销。这个功能支持带有聚合函数+GROUP BY或者聚合函数+DISTINCT或者DISTINCT的SQL使用。
这个功能类似于 INDEX_SKIP_SCAN,做一次对相同的 key 值进行 skip 动作,即可以跳过了索引上相同的段, 这样相比较与索引扫描而言,减少了很多的索引扫描,索引稀疏性越好,性能就会相对更好。
下面用一个简单的例子来说明GROUP_INDEX_SKIP_SCAN怎么应用在优化器。
CREATE TABLE t1(c1 INT, c2 INT, c3 INT, c4 INT);
CREATE UNIQUE INDEX i1_t1 ON t1(c1,c2,c3);
INSERT INTO t1 VALUES (1,1,1,1), (1,1,2,2), (1,3,3,3), (1,4,4,4), (1,5,5,5),
(2,1,1,1), (2,2,2,2), (2,3,3,3), (2,4,4,4), (2,5,5,5);
INSERT INTO t1 SELECT c1, c2, c3+5, c4+10 FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+10, c4+20 FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+20, c4+40 FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+40, c4+80 FROM t1;
ANALYZE TABLE t1;
-- 下面的结果用到了group index skip scan,扫描方式是RANGE SCAN。
greatsql> explain SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | t1 | NULL | range | i1_t1 | i1_t1 | 10 | NULL | 3 | 100.00 | Using index for group-by |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
/* 运行过程:
1、先找到第一列的唯一值,得到结果为1和2
SELECT distinct c1 FROM t1;
2、根据第一列的分组结果在分组内对c2执行范围扫描
SELECT c1, MIN(c2) FROM t1 WHERE c1 = 1;
SELECT c1, MIN(c2) FROM t1 WHERE c1 = 2;*/
-- 如果没有联合索引,那么这条sql内部要创建临时表用于数据的临时处理,扫描方式是全表扫描。可见用GROUP_INDEX_SKIP_SCAN方式执行groupby可以节省空间和开销,提升执行效率。
greatsql> DROP INDEX i1_t1 ON t1;
greatsql> explain SELECT c1, MIN(c2) FROM t1 GROUP BY c1;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
| 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 160 | 100.00 | Using temporary |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-----------------+
二、get_best_group_min_max代码解释
group index skip scan是根据条件和聚合函数列以及distinct列,以及查找对应的联合索引进行判定能不能用group index skip scan,其中联合索引中聚合函数列或者distinct列之前的列为prefix列,即用来作为分组的依据的列,如果有多个除了第一列之外的范围列,那么选取遇到的第一个聚合函数列之前的列为前缀进行分组。
group index skip scan功能没有相关可以开启关闭的关键词,默认满足条件就可以使用,因此如果不想使用就要改变条件或者group by分组,只要不满足条件就不会使用联合索引。
int test_quick_select() {
// 先判断GROUP_INDEX_SKIP_SCAN是否满足条件,不满足的话执行索引合并。
AccessPath *group_path = get_best_group_min_max();
// 不满足GROUP_INDEX_SKIP_SCAN执行索引合并,相关知识看之前知识介绍
}
AccessPath *get_best_group_min_max() {
// 遍历表所有索引,找到联合索引
for (uint cur_param_idx = 0; cur_param_idx < param->keys; ++cur_param_idx) {
// 处理group by语句
if (!join->group_list.empty()) {
// 找到联合索引内的列字段相关mm tree
if (tree) cur_tree = get_index_range_tree(cur_index, tree, param);
// 该条件是否是等号条件或者IS NULL条件
is_eq_range_pred = !(r
GreatSQL优化器之GROUP_INDEX_SKIP_SCAN

最低0.47元/天 解锁文章
556

被折叠的 条评论
为什么被折叠?



