Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


10.2.1.13 条件过滤

在连接处理中,前缀行是从一个表传递到下一个表的行。在一般情况下,优化器尝试将低前缀计数的表早于连接顺序,以保持行组合数量不快速增加。尽量使用关于来自一个表和传递给下一个表的行的条件信息,使得优化器可以更准确地计算行估算并选择最佳执行计划。

没有条件过滤,前缀行计数对一个表基于优化器选择的访问方法估算的行数。条件过滤使优化器能够使用WHERE子句中的其他相关条件,不被访问方法所考虑,从而改善其前缀行计数估算。例如,即使存在索引访问方法可以从当前表中选择行,但也可能有在WHERE子句中对当前表的额外条件来过滤(进一步限制)传递给下一个表的合格行。

一个条件只会贡献到过滤估算当:

  • 它引用当前表。

  • 它依赖于前序连接顺序中的常量值或值。

  • 它不被访问方法所考虑。

EXPLAIN 输出中,rows 列表示选择的访问方法的行估计,filtered 列反映了条件过滤的影响。filtered 值以百分比表示。最大值为 100,表示没有对行进行过滤。从 100 下降的值表示增加的过滤量。

前缀行计数(当前表在 join 到下一个表中的行估计)是 rowsfiltered 值的乘积。也就是说,前缀行计数是估计的行计数减去估计的过滤效果。例如,如果 rows 是 1000,filtered 是 20%,则条件过滤将估计的行计数从 1000 减少到 1000 × 20% = 1000 × .2 = 200。

考虑以下查询:

SELECT *
  FROM employee JOIN department ON employee.dept_no = department.dept_no
  WHERE employee.first_name = 'John'
  AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01';

假设数据集具有这些特征:

  • 员工表有 1024 行。

  • 部门表有 12 行。

  • 两个表都有一个索引在 dept_no 上。

  • 员工表有一个索引在 first_name 上。

  • 8 行满足这个条件在 employee.first_name 上:

    employee.first_name = 'John'
  • 150 行满足这个条件在 employee.hire_date 上:

    employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01'
  • 1 行同时满足这两个条件:

    employee.first_name = 'John'
    AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01'

没有条件过滤时,EXPLAIN 生产的输出类似这样:

+----+------------+--------+------------------+---------+---------+------+----------+
| id | table      | type   | possible_keys    | key     | ref     | rows | filtered |
+----+------------+--------+------------------+---------+---------+------+----------+
| 1  | employee   | ref    | name,h_date,dept | name    | const   | 8    | 100.00   |
| 1  | department | eq_ref | PRIMARY          | PRIMARY | dept_no | 1    | 100.00   |
+----+------------+--------+------------------+---------+---------+------+----------+

对于 employeename 索引的访问方法选择了 8 个匹配名称为 'John' 的行。没有过滤操作(filtered 是 100%),所以所有行都是前缀行:前缀行数是 rows × filtered = 8 × 100% = 8。

在条件过滤的情况下,优化器还考虑了 WHERE 子句中未被访问方法所考虑的条件。在这个情况下,优化器使用 heuristics 估算 BETWEEN 条件对 employee.hire_date 的过滤效果为 16.31%。结果,EXPLAIN 产生的输出类似这样:

+----+------------+--------+------------------+---------+---------+------+----------+
| id | table      | type   | possible_keys    | key     | ref     | rows | filtered |
+----+------------+--------+------------------+---------+---------+------+----------+
| 1  | employee   | ref    | name,h_date,dept | name    | const   | 8    | 16.31    |
| 1  | department | eq_ref | PRIMARY          | PRIMARY | dept_no | 1    | 100.00   |
+----+------------+--------+------------------+---------+---------+------+----------+

现在前缀行数是 rows × filtered = 8 × 16.31% = 1.3,更加接近实际数据集。

通常情况下,优化器不计算最后一个连接表的条件过滤效果(前缀行数减少)因为没有下一个表可以将行传递给。只有在 EXPLAIN 时例外:为了提供更多信息,计算所有连接表的过滤效果,包括最后一个。

控制优化器是否考虑额外的过滤条件,使用condition_fanout_filteroptimizer_switch系统变量中的第10.9.2节,“可切换优化”的标志(见第10.9.2节,“可切换优化”)。这个标志默认启用,但可以禁用以抑制条件过滤(例如,如果某个查询在不使用它时性能更好)。

如果优化器高估了条件过滤的效果,性能可能比不使用它差。在这种情况下,这些技术可能有帮助:

  • 如果一个列没有索引,那么索引它,以便优化器有关于该列值分布的信息并且可以改善其行估计。

  • 类似,如果没有列直方图信息,生成直方图(见第10.9.6节,“优化器统计”)。

  • 更改连接顺序。实现这个目的的方法包括连接顺序优化提示(见第10.9.3节,“优化器提示”)、STRAIGHT_JOIN紧跟在SELECT后,以及STRAIGHT_JOIN连接操作符。

  • 禁用会话中的条件过滤:

    SET optimizer_switch = 'condition_fanout_filter=off';

    或者,使用优化器提示,对于给定的查询:

    SELECT /*+ SET_VAR(optimizer_switch = 'condition_fanout_filter=off') */ ...