MySQL 8.0 索引跳跃扫描
前言
MySQL 8.0 之索引跳跃扫描(Index Skip Scan)是一种优化查询效率的技术,在某些索引查询场景下能够显著提高查询效率。
索引跳跃扫描技术是在使用多列索引查询时,通过跳过一部分索引列而直接进入上下文扫描阶段,以减少扫描的数据行数,从而提高查询效率的一种优化手段。
具体来说,就是通过构建一个覆盖某些索引列的联合索引,然后将查询条件分为两个部分:一部分匹配联合索引的前缀列,一部分匹配联合索引的后缀列。这样就可以跳过索引前缀列扫描,直接进入后缀列扫描,称之为索引跳跃扫描。
示例
官方给出的例子:
验证环境:
MySQL 8.0.30-arm64
执行
可以看到,在 MySQL 8.0 版本中,确实使用了 Using index for skip scan。
底层分析
执行规则
官方的解释:
A range scan is more efficient than a full index scan, but cannot be used in this case because there is no condition on f1
, the first index column. However, as of MySQL 8.0.13, the optimizer can perform multiple range scans, one for each value of f1
, using a method called Skip Scan that is similar to Loose Index Scan
范围扫描比全索引扫描更有效,但在这种情况下不能使用,因为第一个索引列 f1 上没有条件。然而,从 MySQL 8.0.13 开始,优化器可以执行多次范围扫描,每个 f1 值一次,使用一种称为“跳过扫描”的方法,该方法类似于“松散索引扫描”
执行步骤:
Skip between distinct values of the first index part,
f1
(the index prefix).
在第一个索引部分 f1(索引前缀)的不同值之间跳转。
Perform a subrange scan on each distinct prefix value for the
f2 > 40
condition on the remaining index part.
对剩余索引部分上 f2 > 40 条件的每个不同前缀值执行子范围扫描。
按上面的例子执行效果类似于:
For the data set shown earlier, the algorithm operates like this:
Get the first distinct value of the first key part (
f1 = 1
).Construct the range based on the first and second key parts (
f1 = 1 AND f2 > 40
).Perform a range scan.
Get the next distinct value of the first key part (
f1 = 2
).Construct the range based on the first and second key parts (
f1 = 2 AND f2 > 40
).Perform a range scan.
第一步:
获取联合索引第一列去重值,类似于(实际 MySQL 的实现方式应该会有统计器)
这里得到结果:
f1 = 1;
f1 = 2;
第二步:
执行范围查询
其实还有第三步:
合并多次查询的结果
需要满足的条件(8 条)
This Skip Scan access method is applicable under the following conditions:
Table T has at least one compound index with key parts of the form ([A_1, ..., A_
k
,] B_1, ..., B_m
, C [, D_1, ..., D_n
]). Key parts A and D may be empty, but B and C must be nonempty.
表 T 具有至少一个复合索引,其关键部分的形式为([A_1,...,A_k,]B_1,...,B_m,C[,D_1,...,D_n])。关键部分 A 和 D 可以为空,但 B 和 C 必须非空。
The query references only one table.
查询仅引用一张表
The query does not use
GROUP BY
orDISTINCT
.
查询不能使用 GROUP BY
或者 DISTINCT
.
The query references only columns in the index.
该查询仅引用索引中的列。
The predicates on A_1, ..., A_
k
must be equality predicates and they must be constants. This includes the IN() operator.
A_1, ..., A_k 上的谓词必须是相等谓词并且它们必须是常量。这包括 IN() 运算符。
The query must be a conjunctive query; that is, an
AND
ofOR
conditions:(cond1(key_part1) OR cond2(key_part1)) AND (cond1(key_part2) OR ...) AND ...
查询必须是联合查询;即 OR 条件的 AND: (cond1(key_part1) OR cond2(key_part1)) AND (cond1(key_part2) OR ...) AND ...
There must be a range condition on C.
C 必须有一个范围条件。
Conditions on D columns are permitted. Conditions on D must be in conjunction with the range condition on C.
D 列上的条件是允许的。 D 上的条件必须与 C 上的范围条件结合起来。
验证条件
条件一:
这个时候,索引不能使用主键索引(主键索引的所有字段不能为空),只能建立二级索引。
也验证了索引跳跃不只在主键索引上生效(了解索引树的结构,索引跳跃应该是作用在索引树上的优化)
按照条件 1,索引的 A,D 可以为空,索引的中间部分(f2,f3)不能为空
步骤 1:
上述,表中无空值,索引条件 1 不满足(也可能理解的不对)也使用了索引跳跃
步骤 2:
表中添加空值数据,索引条件 1 不满足(也可能理解的不对)又使用了索引跳跃
步骤 3:
条件二:
规避空值影响:
子查询:
join 联表:
条件三:
条件四:
条件五:
非常数的场景,目前想不到怎么构建(一时间脑子空了,后面再补吧)
如果是这种,就多表了
条件六:
条件七:
步骤 1:
这里很奇怪,key(f1, f2, f3, f4) , f2 = 40 有索引跳跃扫描,但是 f3 = 40,没有索引跳跃
条件八:
这个条件不是太理解,没有太看懂什么意思,后面补充一下。
总结
从上面的例子,MySQL 8.0 索引跳跃扫描是作用在索引树上的范围扫描优化。优化器可以执行多次范围扫描。
参考文档
MySQL 官方文档
https://dev.mysql.com/doc/refman/8.0/en/range-optimization.html#range-access-skip-scan
版权声明: 本文为 InfoQ 作者【红袖添香】的原创文章。
原文链接:【http://xie.infoq.cn/article/70007fda6593b6d72e6ac492d】。文章转载请联系作者。
评论