• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 条件过滤

    在联接处理中,前缀行是从联接中的一个表传递到下一个表的那些行。通常,优化器会尝试在连接顺序的早期放置前缀计数较低的表,以防止行组合的数量迅速增加。在某种程度上,优化器可以使用有关从一个表中选择并传递到下一个表的行的条件的信息,它可以更准确地计算行估计并选择最佳执行计划。

    如果不使用条件过滤,则表的前缀行数将WHERE根据优化器选择的访问方法,根据子句选择的估计行数进行计算。条件过滤使优化器可以WHERE在访问方法未考虑的子句中使用其他相关条件,从而改善其前缀行数估计。例如,即使可能存在基于索引的访问方法,该方法可用于在联接中从当前表中选择行,但在表中可能还存在其他条件。WHERE子句可以过滤(进一步限制)传递给下一张表的合格行的估计值。

    仅在以下情况下,条件才有助于过滤估计:

    • 它引用当前表。
    • 它取决于连接序列中一个或多个常量值。
    • 访问方法尚未考虑它。

    EXPLAIN输出中,该rows列指示所选访问方法的行估计,该filtered列反映条件过滤的效果。filtered值以百分比表示。最大值为100,这表示未过滤行。值从100减小表示过滤量增加。

    前缀行数(估计从当前表通过联接传递到下一个表的行数)是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';
    

    假设数据集具有以下特征:

    • employee表有1024行。
    • department表有12行。
    • 两个表在上都有一个索引dept_no
    • employee表的索引为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了访问方法未考虑的子句中的条件。在这种情况下,优化器使用启发式方法估计BETWEEN条件为的16.31%的过滤效果employee.hire_date。结果,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_filter标志optimizer_switch(请参见“可切换的优化”)。默认情况下,此标志是启用的,但可以禁用它以抑制条件过滤(例如,如果发现特定查询不使用它会产生更好的性能)。

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

    • 如果未对列进行索引,请对其进行索引,以便优化程序获得有关列值分布的一些信息,并可以改善其行估计。
    • 同样,如果没有列直方图信息可用,则生成直方图(请参见“优化器统计”)。
    • 更改加入顺序。完成此操作的方法包括紧接在和之后的联接顺序优化器提示(请参见“优化器提示”)。STRAIGHT_JOINSELECTSTRAIGHT_JOIN
    • 禁用会话的条件过滤:

      SET optimizer_switch = 'condition_fanout_filter=off';
      

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

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