Semi-join LooseScan 子查询优化策略

本篇为子查询优化系列第五篇,参考自 MariaDB 博客: https://mariadb.com/kb/en/loosescan-strategy/,建议先看 MySQL子查询优化 和 Semi-join Materialization 子查询优化策略

实现思路

LooseScan 是子查询 semijoin 优化的一种实现策略(还是为了去重)。下面通过一个示例来说明其实现原理,假如有个SQL要查询有卫星的国家:

select * from Country  
where 
  Country.code in (select country_code from Satellite)

Satellite 表上的 country_code 字段有索引,使用这个索引可以得到有序的(country_code,satellite_code)数据:

LooseScan 策略实际上要的不是排序,而是分组效果:卫星数据以 country_code 为组,一组一组的展示,比如澳大利亚的卫星都汇聚在一起,不会和其他国家的卫星混在一起。这样一来,就可以很轻松的从每个组中取出一颗卫星以及其所在的国家,而且没有重复值,然后再与 Country 进行联接:

执行成本

下面我们换一个查询来展示 Semi-join LooseScan 优化策略的执行计划,以及扫描行数。查询员工号小于 10277 的员工所在的部门:

select dept_name from departments where dept_no in \
(select dept_no from dept_emp where emp_no<10227);
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: dept_emp
   partitions: NULL
         type: index
possible_keys: PRIMARY,dept_no
          key: dept_no
      key_len: 4
          ref: NULL
         rows: 331570
     filtered: 0.00
        Extra: Using where; Using index; LooseScan
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: departments
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 4
          ref: employees.dept_emp.dept_no
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.01 sec)

执行计划 Extra 列中驱动表所在行会有 LooseScan 标识。其执行流程为:

  1. 对驱动表的关联字段 dept_no 所在索引进行全索引扫描,分组后每组取出第一行记为 R(这里扫描行数为全表行数 331570,这里即使 emp_no 字段有索引也没法使用,除非不用LooseScan策略);
  2. 从R 中取出 dept_no 字段值,与 departments 进行联接,符合条件的行放入结果集,这里走索引查找,每次扫描 1 行;
  3. 因为一共有 9 个部门,第 1 步分组后得到 9 行R,所以第 2 步重复9次,直到结束。

一共扫描行数为 331570+9*1.

小结

  1. 在执行计划中,LooseScan 策略显示在驱动表的 Extra 信息中 LooseScan;
  2. 通过参数 optimizer_switch 中的 semijoin=on 和 loosescan=on 开启 Semi-join LooseScan,默认就是开启的。

你可能感兴趣的:(Semi-join LooseScan 子查询优化策略)