在联接处理中,前缀行是从联接查询中的一个表传递到下一个表的那些行。
通常,优化器会尝试在联接查询的早期放置前缀计数较低的表,以防止行组合的数量快速增加。
在某种程度上,优化器可以使用从一个表中选择并传递到下一个表的行的条件信息,它就可以越准确地计算行评估并选择最佳执行计划。
在没有条件过滤的情况下,表的前缀行计数基于WHERE子句根据优化器选择的任何访问方法选择的估计行数。条件过滤使优化器能够在WHERE子句中使用访问方法未考虑的其他相关条件,从而改进其前缀行计数估计。
例如,即使可能有一种基于索引的访问方法可以用于在联接中从当前表中选择行,WHERE子句中也可能有针对该表的附加条件,可以过滤(进一步限制)传递给下一个表的符合条件的行的估计数。
博主PS:阅读了"嵌套联接优化"博客的朋友应该可以联想到,只要控制外层循环的条件满足的次数或概率,就能减少内层for循环的次数。上文中的“(进一步限制)”有类似的作用
【MySQL精通之路】SQL优化(1)-查询优化(7)-嵌套循环联接-CSDN博客
只有在以下情况下,条件才有助于滤波估计:
1.它指的是当前表。
2.它取决于联接序列中早期表中的一个或多个常数值。
3.访问方法尚未将其考虑在内。
在EXPLAIN输出中,rows列表示所选访问方法的行估计,而filtered列反映条件筛选的效果。filtered值以百分比表示。最大值为100,这意味着没有对行进行筛选。从100开始递减的值表示过滤量的增加。
前缀行计数(估计从联接中的当前表传递到下一个表的行数)是行和筛选值的乘积。
也就是说,前缀行计数是估计的行计数,减去估计的过滤效果。例如,如果行数为1000,过滤为20%,则条件过滤会将估计的行数1000减少为前缀行数1000×20%=1000×.2=200。
请考虑以下查询:
SELECT *FROM employee JOIN department ON employee.dept_no = department.dept_noWHERE employee.first_name = 'John'AND employee.hire_date BETWEEN '2018-01-01' AND '2018-06-01';
假设数据集具有以下特征:
员工表有1024行。
部门表有12行。
两个表都有dept_no的索引。
employee表在first_name上有一个索引。
employee.first_name上有8行满足此条件:
employee.first_name = 'John'
employee.hire_date上有150行满足此条件:
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 |
+----+------------+--------+------------------+---------+---------+------+----------+
对于employee,名称索引上的访问方法会拾取与名称“John”匹配的8行。没有进行筛选(已筛选为100%),因此所有行都是下一个表的前缀行:前缀行数为行×已筛选=8×100%=8。
通过条件筛选,优化器还会考虑WHERE子句中访问方法未考虑的条件。在这种情况下,优化器使用启发式方法来估计employee.hire_date上的BETWEEN条件的过滤效果为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 |
+----+------------+--------+------------------+---------+---------+------+----------+
现在前缀行数是行×过滤=8×16.31%=1.3,这更能反映实际数据集。
通常,优化器不会计算最后一个联接表的条件过滤效果(前缀行数减少),因为没有下一个表可以将行传递到。
EXPLAIN出现异常:为了提供更多信息,将计算所有联接表的过滤效果,包括最后一个表。
要控制优化器是否考虑其他过滤条件,请使用optimizer_switch系统变量的condition_fanout_filter标志(请参阅“可切换优化”)。
默认情况下会启用此标志,但可以禁用此标志以抑制条件筛选(例如,如果发现某个特定查询在没有条件筛选的情况下可以获得更好的性能)。
如果优化器高估了条件筛选的效果,则性能可能比不使用条件筛选时更差。
在这种情况下,这些技术可能有助于:
如果列没有索引,请对其进行索引,这样优化器就可以获得有关列值分布的一些信息,并可以改进其行估计值。
同样,如果没有可用的列直方图信息,则生成直方图(参见第10.9.6节“优化器统计”)。
更改联接顺序。实现这一点的方法包括联接顺序优化器提示(请参阅第10.9.3节“optimizer提示”)、SELECT之后的STRIGHT_join以及STRIGHT_ join联接运算符。
禁用会话的条件筛选:
SET optimizer_switch = 'condition_fanout_filter=off';
或者,对于给定的查询,使用优化器提示:
SELECT /*+ SET_VAR(optimizer_switch = 'condition_fanout_filter=off') */ ...