MYSQL Skip Scan Range 小功能,解决大问题 ?

  • 2019 年 12 月 12 日
  • 筆記

用过MYSQL的都会被别的数据库的operation 吐槽,索引的建立与使用方面的需要掌握的知识是比较“矫情的”。为什么这么说,在MYSQL 5.X中如果一个表中 有这样的索引,和这样的查询,索引的效率就会大打折扣。

我们来看一下,根据官方的文档我们创建下面的数据

请准备MYSQL 8.013以上版本 以及 MYSQL 5.7 版本的两台机器,并执行下面的操作

CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRIMARY KEY(f1, f2));  INSERT INTO t1 VALUES    (1,1), (1,2), (1,3), (1,4), (1,5),    (2,1), (2,2), (2,3), (2,4), (2,5);  INSERT INTO t1 SELECT f1, f2 + 5 FROM t1;  INSERT INTO t1 SELECT f1, f2 + 10 FROM t1;  INSERT INTO t1 SELECT f1, f2 + 20 FROM t1;  INSERT INTO t1 SELECT f1, f2 + 40 FROM t1;  ANALYZE TABLE t1;

EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;

set session optimizer_trace=’enabled=on';

然后我们通过optimizer_trace 来查看这两个服务器上查询上面的给出的分析结果

下面的图形仅仅只给出不同的截图(具体 optimize_trace 之前有文字写过,这里就不赘述了)


1 MYSQL 5.7 的 row_estimation ,我看可以看到很简单

2 下面是MYSQL 8.017 的图,从下图看,明显的MYSQL 8 在查询计划的分析要比 MYSQL 5.7 复杂的多,其中第二张图已经显示走了skip_scan

既然看到不同,但问题是这样有什么用,首先如果是MYSQL 5.7 上基本上走的是 INDEX Scan 而, 而在MYSQL 8 上做的事情要远远多于 MYSQL5.7 从上图可以看出,首先查询先将索引中的前边的字段,进行了group by 的操作,将需要进行扫描的数据通过第一个字段划分了块,然后在每个块中扫描range 的数据。

这样的好处也是显而易见的,如果将数据扫描进行分块处理,有些不包含range 的数据块将不被扫描,或者不包含range 的行也将不被扫描。

这项功能也是有一定要求的

1 必须单表

2 不能有group distinct 的操作

3 索引两边的字段都可以包含NULL ,但中间的字段不可以有NULL

下面在做一个测试确认一下前边有两个字段的情况下,是不是也是可以走skip scan index

CREATE TABLE t2 (f1 INT NOT NULL, f2 INT NOT NULL,f3 int not null);

INSERT INTO t2 VALUES

(1,1,2), (1,2,3), (1,3,4), (1,4,5), (1,5,6),

(2,1,3), (2,2,4), (2,3,6), (2,4,2), (2,5,4);

INSERT INTO t2 SELECT f1, f2 + 5,f3 + 2 FROM t2;

INSERT INTO t2 SELECT f1, f2 + 10,f3 + 4 FROM t2;

INSERT INTO t2 SELECT f1, f2 + 20, f3 + 5 FROM t2;

INSERT INTO t2 SELECT f1, f2 + 4, f3 + 6 FROM t2;

ANALYZE TABLE t1;

create index ix_t2_f2_f3 on t2 (f1,f2,f3);

最终的结果还是可以走的,其实可以理解为,前边将两个没有条件的字段都变为有具体值的条件匹配后面的字段的范围查询。这个做法在ORACLE 早就是有的功能,目前MYSQL 也继承了这个功能。