干货 | 解读MySQL 8.0新特性:Skip Scan Range

干货 | 解读MySQL 8.0新特性:Skip Scan Range
最新回答
消失在我眼中

2026-07-26 09:55:39

MySQL 8.0的Skip Scan Range(Loose Skip Scan)特性解析

MySQL 8.0.13版本引入了由Facebook贡献的Loose Skip Scan优化技术,该技术改进了复合索引的使用方式,允许在特定条件下更高效地利用索引进行范围查询。以下是对该特性的详细解读:

1. 核心功能
  • 传统索引限制:在MySQL 8.0之前,若复合索引为idx(col1, col2),查询条件仅包含col2(如WHERE col2 > 10)时,无法直接利用索引的有序性,需全索引扫描后过滤,效率低下。
  • Skip Scan优化:新特性允许对复合索引的前缀列(如col1)进行分组,对每个前缀值单独执行范围扫描(如col1=1 AND col2 > 10、col1=2 AND col2 > 10等),避免全表扫描。
2. 示例分析

以官方文档的示例表t1(f1 INT, f2 INT, PRIMARY KEY(f1, f2))为例:

-- 插入数据后,执行查询EXPLAIN SELECT f1, f2 FROM t1 WHERE f2 > 40;
  • 执行计划:Extra字段显示Using index for skip scan,表明使用了Skip Scan。
  • 优化器日志:通过optimizer_trace可观察到:

    潜在索引选择:PRIMARY键被评估为候选。

    分组数(num_groups):前缀列f1的不同值数量(如示例中为3)。

    范围条件:f2 > 40被拆分为多个子范围扫描。

3. 执行流程(InnoDB视角)
  1. 初始化扫描:从索引左侧开始。
  2. 构建搜索元组:

    对每个f1的值(如1、2),构建元组(f1, 40)定位起始点。

    扫描至f2范围结束,再读取下一个f1值(如从1跳到2)。

  3. 日志输出:

    STEP 2:元组(1, 40)开始扫描。

    STEP 3:前缀(1)单独扫描以定位下一组。

    STEP 4:元组(2, 40)继续扫描。

4. 适用条件

Skip Scan仅在满足以下条件时触发(参考get_best_skip_scan()逻辑):

  • 索引结构:复合索引形如<A_1,...,A_k, B_1,...,B_m, C, [D_1,...,D_n]>,其中B和C非空。
  • 查询限制:

    单表查询,无GROUP BY或DISTINCT。

    仅引用索引列(覆盖索引)。

    前缀列A_1...A_k需为等值条件(如=或IN)。

    查询为合取范式(AND连接的OR条件)。

    存在对C列的范围条件(如>, <, BETWEEN)。

    可附加对D列的条件(需与C的范围条件共存)。

5. 性能与控制
  • 性能提升:避免全索引扫描,尤其在前缀列区分度低时(如f1值较少)效果显著。
  • 控制方式:

    Hint:通过/*+ SET_VAR(optimizer_switch='skip_scan=on') */显式启用。

    全局参数:optimizer_switch中的skip_scan默认开启。

  • 成本计算:cost_skip_scan()基于索引统计信息(如rec_per_key估算不同前缀值的行数)选择最优方案。
6. 注意事项
  • 统计信息准确性:若前缀列的distinct value估算不准确,可能导致次优计划。
  • 复杂查询限制:多表连接、非索引列引用等场景不适用。
总结

Skip Scan通过智能拆分复合索引的范围查询,显著提升了特定场景下的执行效率。理解其适用条件与内部机制,有助于在SQL优化中充分利用这一特性。