mysql 调优 来自5.6版本官方手册

注意:下面示例中的key1和key2代表两个索引,key_part1和key_part2代表一个复合索引的第一列和第二列。non_key代表非索引列。

优化SQL语句

where语句优化:

mysql会自动执行的一些优化有:

  • 删除不必要的括号:
  • 常量条件替换

(a<b AND b=c) AND a=5

-> b>5 AND b=c AND a=5

  • 常量条件去除

(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6)

-> B=5 OR B=6

  • 索引使用的常量表达式只计算一次。
  • 检测无效常数表达式。 MySQL快速检测到一些SELECT语句是不可能的,并且不返回任何行。
  • 在查询中的任何其他表之前首先读取所有常量表。 常数表是以下任何一种:

一个空的表或一行的表。

与PRIMARY KEY或UNIQUE索引中的WHERE子句一起使用的表,其中所有索引部分与常量表达式进行比较,并将其定义为NOT NULL。

所有以下表格均用作常规表格:

SELECT * FROM t WHERE primary_key=1;

SELECT * FROM t1,t2

WHERE t1.primary_key=1 AND t2.primary_key=t1.id;

  • 如果有ORDER BY子句和不同的GROUP BY子句,或者如果ORDER BY或GROUP BY包含来自连接队列中第一个表以外的表的列,则会创建一个临时表。
  • 如果使用SQL_SMALL_RESULT修饰符,MySQL使用内存中临时表。
  • 查询每个表索引,并使用最佳索引,除非优化器认为使用全表扫描更有效。在曾经的历史版本中,根mysql据最佳索引是否超过表格的30%,使用全表扫描,但固定百分比不再决定使用索引或全表扫描之间的选择。优化器现在更加复杂,并且基于其他因素,如表大小,行数和I / O块大小。
  • 在某些情况下,MySQL可以从索引中读取行,甚至不咨询数据文件。

一些非常快的查询示例:

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name

WHERE key_part1=constant;

SELECT ... FROM tbl_name

ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name

ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

MySQL仅使用索引树来解析以下查询:

SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;

SELECT COUNT(*) FROM tbl_name

WHERE key_part1=val1 AND key_part2=val2;

SELECT key_part2 FROM tbl_name GROUP BY key_part1;

范围查询优化:

单列索引的范围访问方法:

使用=, IN(),IS NULL,IS NOT NULL运算符时, >,<,> =,<=,BETWEEN,!=或<>运算符时,关键部分与常量值的比较是范围条件,如果参数为LIKE,则比较LIKE是不是以通配符开头的常量字符串。

MySQL尝试从WHERE子句中为每个可能的索引提取范围条件。 在提取过程中,不能用于构建范围条件的条件下降,产生重叠范围的条件组合,并且消除产生空范围的条件。

考虑以下语句,其中key1是索引列,而非键未被索引:

SELECT * FROM t1 WHERE

  (key1 <'abc'AND(key1 LIKE'abcde%'OR key1 LIKE'%b'))OR

  (key1 <'bar'AND nonkey = 4)OR

  (key1 <'uux'AND key1>'z');

范围索引key1的提取过程如下:

  1. 从原来的WHERE子句开始:

(key1 <'abc'AND(key1 LIKE'abcde%'OR key1 LIKE'%b'))OR

(key1 <'bar'AND nonkey = 4)OR

(key1 <'uux'AND key1>'z')

  1. 删除nonkey = 4和key1 LIKE'%b',因为它们不能用于范围扫描(将使用using where)。删除它们的正确方法是用TRUE替换它们,以便在执行范围扫描时不要错过任何匹配的行。用TRUE替代它们,我们得到:

(key1 <'abc'AND(key1 LIKE'abcde%'OR TRUE))OR

(key1 <'bar'AND TRUE)OR

(key1 <'uux'AND key1>'z')

  1. 折叠始终为真或假的条件:

(key1 LIKE'abcde%'OR TRUE)始终为真

(key1 <'uux'AND key1>'z')始终为false

  1. 用常数替换这些条件,我们得到:

(key1 <'abc'AND TRUE)OR(key1 <'bar'AND TRUE)OR(FALSE)

  1. 删除不必要的TRUE和FALSE常量,我们得到:

(key1 <'abc')OR(key1 <'bar')

  1. 将重叠的间隔组合成一个产生用于范围扫描的最终条件:

(key1 <'bar')

复合索引的范围访问方法

复合索引上的范围条件将索引行限制在一个或多个关键元组间隔内。 关键元组间隔是通过一组关键元组定义的,使用索引的顺序。

例如,考虑定义为key1(key_part1,key_part2,key_part3)的复合索引以及按键顺序列出的以下集合的关键元组:

key_part1 key_part2 key_part3

NULL 1 'abc'

NULL 1 'xyz'

NULL 2 'foo'

1 1 'abc'

1 1 'xyz'

1 2 'abc'

2 1 'aaa'

条件key_part1 = 1定义了此间隔:

(1,-inf,-inf) <= (key_part1,key_part2,key_part3) < (1,+inf,+inf)

该区间涵盖上述数据集中的第4,5,6组,可以通过范围访问方式使用。

相反,条件key_part3 ='abc'不定义单个间隔,范围访问方法不能使用。

以下说明详细说明了范围条件如何适用于复合索引。

只要比较运算符为=,<=>或IS NULL,优化器将尝试使用其他关键部件来确定间隔。 如果操作符是>,<,> =,<=,!=,<>,BETWEEN或LIKE,则优化程序使用它,但不再考虑关键部分。 对于以下表达式,优化器使用来自第一个比较的=。 它也使用第二个比较中的> =,但不考虑其他关键部分,并且不对间隔构造使用第三个比较:

key_part1 = 'foo' AND key_part2 >= 10 AND key_part3 > 10

它的间隔为:

('foo',10,-inf) < (key_part1,key_part2,key_part3) < ('foo',+inf,+inf)

创建的间隔可能包含比初始条件更多的行。 例如,前面的间隔包含不满足原始条件的值('foo',11,0)。

如果涵盖间隔中包含的行集合的条件与OR组合,则它们形成覆盖其间隔的并集中包含的一组行的条件。 如果条件与AND组合,则它们形成一个覆盖其间隔的交集中包含的一组行的条件。 例如,对于两部分指数的这种情况:

(key_part1 = 1 AND key_part2 < 2) OR (key_part1 > 5)

它的间隔为:

(1,-inf) < (key_part1,key_part2) < (1,2)

(5,-inf) < (key_part1,key_part2)

在这个例子中,第一行的间隔用于左边界的一个key部分和右边界的两个key部分。 第二行的间隔仅使用一个key部分。 EXPLAIN输出中的key_len列表示使用的key前缀的最大长度。

在某些情况下,key_len这可能不是您期望的。 假设key_part1和key_part2可以为NULL。 然后key_len列显示以下条件的两个key部分长度:

key_part1 >= 1 AND key_part2 < 2

实际上,它的范围条件被转换为了:

key_part1 >= 1 AND key_part2 IS NOT NULL

多值比较的均值范围优化。

如果col_name是索引列,那么在执行如下查询的时候:

col_name IN(val1, ..., valN)

col_name = val1 OR ... OR col_name = valN

优化器估计读取符合条件行的成本,以进行相等范围比较,如下所示:

如果col_name上有唯一索引,则每个范围的行估计值为1,因为最多一行可以具有给定的值。

否则,col_name上的任何索引都是非唯一的,优化器可以使用潜入索引或索引统计信息来估计每个范围的行计数。

也就是说,要对每一个条件都询问一下存储引擎评估的行数,例如,表达式col_name IN(10,20,30)具有三个相等的范围,并且优化器对每个范围进行两次潜水以生成行估计。每对潜水产生具有给定值的行数的估计。

索引潜水提供准确的行估计,但随着表达式中比较值的数量的增加,优化器需要更长的时间来生成行估计。索引统计的使用不如索引潜水准确,但允许更大的价值列表的行估计。

eq_range_index_dive_limit系统变量使您可以配置优化程序从一个行估计策略切换到另一个行估计策略的值的数量。要允许使用索引潜水来比较最多N个相等范围,请将eq_range_index_dive_limit设置为N + 1。要禁用统计信息的使用,并始终使用索引潜水而不考虑N,请将eq_range_index_dive_limit设置为0。

要更新表索引统计信息以获得最佳估计值,请使用ANALYZE TABLE。

索引合并优化

使用多个索引合并来实现查询。

在EXPLAIN输出中,索引方法在type列中显示为index_merge。 在这种情况下,key列包含使用的索引列表,key_len包含这些索引的最长关键部分的列表。

索引合并访问方法有几种算法,显示在EXPLAIN输出的Extra字段中

  • 使用intersect(...)
  • 使用union(...)
  • 使用sort_union(...)

我的理解:Mysql应该是将多个key的条件逐个交给存储引擎去取出数据(应该是只要了主键),然后在服务器层做intersect,union,sort_union操作。

以下部分将更详细地描述这些算法。 优化器根据各种可用选项的成本估算,在不同的可能的索引合并算法和其他访问方法之间进行选择。

The Index Merge Intersection Access Algorithm:

当WHERE子句转换为与AND组合的不同键上的多个范围条件时,此访问算法是适用的。

SELECT * FROM innodb_table

WHERE primary_key < 10 AND key_col1 = 20;

 

SELECT * FROM tbl_name

WHERE (key1_part1 = 1 AND key1_part2 = 2) AND key2 = 2;

The Index Merge Union Access Algorithm

当WHERE子句转换为与OR组合的不同键上的多个范围条件时,此访问算法是适用的。

SELECT * FROM t1

WHERE key1 = 1 OR key2 = 2 OR key3 = 3;

 

SELECT * FROM innodb_table

WHERE (key1 = 1 AND key2 = 2)

OR (key3 = 'foo' AND key4 = 'bar') AND key5 = 5;

The Index Merge Sort-Union Access Algorithm

当WHERE子句转换为由OR组合的几个范围条件时,该访问算法是适用的,但是Index Merge union算法不适用的情况。我理解的Sort-Union只是union的一种实现方式。

SELECT * FROM tbl_name

WHERE key_col1 < 10 OR key_col2 < 20;

 

SELECT * FROM tbl_name

WHERE (key_col1 > 10 OR key_col2 = 20) AND nonkey_col = 30;

 

索引合并优化算法具有以下已知缺陷:

  • 如果您的查询具有复杂的WHERE子句和深度AND / OR嵌套。
  • 索引合并不适用于全文索引。

 

索引条件下推优化

参考以前的文章。

嵌套循环连接算法

假设使用以下连接类型来执行三个表t1,t2和t3之间的连接:

Table Join Type

t1 range

t2 ref

t3 ALL

如果使用简单的NLJ算法,则会像下列那样处理连接:

for each row in t1 matching range {

for each row in t2 matching reference key {

for each row in t3 {

if row satisfies join conditions, send to client

}

}

}

块嵌套循环连接算法(BNL):

块嵌套循环(BNL)连接算法使用在外部循环中读取的行的缓冲来减少内部循环中的表必须被读取的次数。 例如,如果将10行读入缓冲区并将缓冲区传递到下一个内循环,则可以将内循环中读取的每行与缓冲区中的所有10行进行比较。 这将减少内表必须读取次数的一个数量级。

Multi-Range Read Optimization:MRR优化

我的理解:因为对二级索引(非主键聚簇索引)的范围扫描可能产生太多的磁盘随机读,Mysql可能会首先通过索引条件拿到所有满足的Primary Key,然后对Primary Key排序后,再根据Primary Key去读取数据,这样就可以有比较多的磁盘顺序读。

使用MRR时,EXPLAIN输出中的Extra列显示Using MRR。

 IS NULL优化

MySQL可以对col_name IS NULL执行相同的优化,如果它可以用于col_name = constant_value。 例如,MySQL可以使用索引和范围来使用IS NULL搜索NULL。

SELECT * FROM tbl_name WHERE key_col IS NULL;

 

SELECT * FROM tbl_name WHERE key_col <=> NULL;

 

SELECT * FROM tbl_name

WHERE key_col=const1 OR key_col=const2 OR key_col IS NULL;

 

MySQL还可以优化col_name = expr OR col_name IS NULL,使用此优化时,EXPLAIN会显示ref_or_null。

ref_or_null首先在索引列上执行ref,也就是col_name = expr,然后再执行is null,也是在索引上搜索的。

 ORDER BY 优化

本节介绍什么时候MySQL可以使用索引来满足ORDER BY子句,在不能使用索引时使用的filesort算法,以及从优化器提供的有关ORDER BY的执行计划信息。

使用索引来满足ORDER BY

  1. 可以使用索引来排序的sql:

SELECT * FROM t1

ORDER BY key_part1, key_part2;

 

SELECT * FROM t1

WHERE key_part1 = constant

ORDER BY key_part2;

 

SELECT * FROM t1

ORDER BY key_part1 DESC, key_part2 DESC;

 

SELECT * FROM t1

WHERE key_part1 = 1

ORDER BY key_part1 DESC, key_part2 DESC;

 

SELECT * FROM t1

WHERE key_part1 = constant1 AND key_part2 > constant2

ORDER BY key_part2;

在某些情况下,MySQL不能使用索引来解析ORDER BY,尽管它仍然可以使用索引来查找与WHERE子句匹配的行。 例子:

  • 该查询对不同索引使用ORDER BY:

SELECT * FROM t1 ORDER BY key1, key2;

  • 查询在索引的非连续部分使用ORDER BY:

SELECT * FROM t1 WHERE key2=constant ORDER BY key_part1, key_part3;

  • 查询组合ASC和DESC:

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;

  • 用于获取行的索引与ORDER BY中使用的索引不同:

SELECT * FROM t1 WHERE key2=constant ORDER BY key1;

  • 该查询使用ORDER BY与包含除索引列名称之外的术语的表达式:

SELECT * FROM t1 ORDER BY ABS(key);

SELECT * FROM t1 ORDER BY -key;

  • 该查询连接了许多表,并且ORDER BY中的列不是全部来自用于检索行的第一个表
  • 索引是前缀索引将无法使用order by
  1. 排序使用filesort

MySQL具有多个用于排序和检索结果的filesort算法。 原始算法只使用ORDER BY列。 修改的算法不仅使用ORDER BY列,而且使用查询引用的所有列。 还有一个小结果集的算法,使用排序缓冲区作为没有合并文件的优先级队列进行排序。

优化器选择要使用的filesort算法。 它通常使用修改的算法,除非涉及BLOB或TEXT列,在这种情况下,它使用原始算法。 对于每个算法,排序缓冲区(内存中用于排序的区域)大小是sort_buffer_size系统变量值。

  • 原始文件排序算法

原始的filesort算法的工作原理如下:

根据键或表扫描读取所有行。跳过不符合WHERE子句的行。

对于每一行,在排序缓冲区(内存中用于排序的区域)中存储由一对值(排序键值和行ID)组成的元组。

如果所有对都适合排序缓冲区,则不会创建临时文件。否则,当排序缓冲区已满时,在内存中运行快速排序并将其写入临时文件。保存指向排序块的指针。

重复上述步骤,直到读取所有行。

将临时文件中两个排序块合并为一个。

重复上一步骤。

在最后一次多合并时,只将行ID(值对的最后一部分)写入结果文件。

使用结果文件中的行ID按排序顺序读取行。

这种方法的一个问题是它读取行两次:一次在WHERE子句评估期间,并在排序值对之后再次。

  • 改进的文件排序算法

修改的filesort算法结合了一个优化,以避免读取两行:排序键值对应的不是行ID,而记录查询引用的列。修改的filesort算法的工作原理如下:

读取与WHERE子句匹配的行。

对于每一行,在排序缓冲区中存储由排序键值和查询引用的列组成的元组。

当排序缓冲区变满时,通过内存中的排序键值对元组进行排序,并将其写入临时文件。

在合并排序临时文件之后,按排序顺序检索行,但是从排序的元组中直接读取查询所需的列,而不是再次访问该表。

由修改的文件队列算法使用的元组比原始算法使用的对象长,并且更少的匹配在排序缓冲区中。因此,额外的I / O可能使修改后的方法变得更慢,而不是更快。为了避免减速,优化器仅在排序元组中的额外列的总大小不超过max_length_for_sort_data系统变量的值时才使用修改的算法。

  • 内存中的文件排序算法

对于以下表单的查询(和子查询),优化器可以使用filesort来有效地处理内存中的ORDER BY操作,而无需合并文件:

SELECT ... FROM single_table ... ORDER BY non_index_column [DESC] LIMIT [M,]N;

 

这种类型的查询在仅显示较大结果集中的几行的Web应用程序中很常见。例如:

SELECT col1, ... FROM t1 ... ORDER BY name LIMIT 10;

SELECT col1, ... FROM t1 ... ORDER BY RAND() LIMIT 15;

排序缓冲区的大小为sort_buffer_size。如果N行的排序元素足够小以适应排序缓冲区(如果指定了M,则为M + N行),则服务器可以避免使用临时文件并通过将排序缓冲区作为优先级来执行内存中排序队列:

  • 扫描表格,从队列中按排序顺序从每个选定行插入选择列表列。如果队列已满,则会移除排序顺序中的最后一行。
  • 从队列返回前N行。 (如果指定了M,则跳过前M行并返回下一个N行。)

假设表t1有四个VARCHAR列a,b,c和d,并且优化器对此查询使用filesort:

SELECT * FROM t1 ORDER BY a, b;

查询按a和b排序,但返回所有列,因此查询引用的列为a,b,c和d。根据优化器选择的哪个filesort算法,查询执行如下:

对于原始算法,sort buffer元组具有以下内容:

(fixed size a value, fixed size b value,

row ID into t1)

优化器对固定大小值进行排序。排序后,优化器按顺序读取元组,并使用每个元组中的行ID从t1读取行以获取选择列列列值。

对于修改的算法,排序缓冲元组具有以下内容:

(fixed size a value, fixed size b value,

a value, b value, c value, d value)

优化器对固定大小值进行排序。排序完成后,优化器按顺序读取元组,并使用a,b,c和d的值来获取选择列表列值,而不再读取t1。

GROUP BY优化

满足GROUP BY子句的最一般方法是扫描整个表,并创建一个新的临时表,其中来自每个组的所有行都是连续的,然后使用此临时表来发现组并应用聚合函数(如果有的话)。在某些情况下,MySQL能够做得比这更好,并避免创建临时表而使用索引。

GROUP BY仅能使用形成索引最左侧前缀的列,而不指定其他列。 例如,如果表t1在(c1,c2,c3)上有一个索引,如果查询具有GROUP BY c1,c2,则适用松散索引扫描。如果查询具有GROUP BY c2,c3(列不是最左前缀)或GROUP BY c1,c2,c4(c4不在索引中),则不适用。

假设在表t1(c1,c2,c3,c4)上存在索引idx(c1,c2,c3)。松散索引扫描访问方法可用于以下查询:

SELECT c1, c2 FROM t1 GROUP BY c1, c2;

SELECT DISTINCT c1, c2 FROM t1;

SELECT c1, MIN(c2) FROM t1 GROUP BY c1;

SELECT c1, c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;

SELECT MAX(c3), MIN(c3), c1, c2 FROM t1 WHERE c2 > const GROUP BY c1, c2;

SELECT c2 FROM t1 WHERE c1 < const GROUP BY c1, c2;

SELECT c1, c2 FROM t1 WHERE c3 = const GROUP BY c1, c2;由于以下原因,无法使用此快速选择方法执行以下查询:

下列group by不能使用索引:

除了MIN()或MAX()之外还有汇总功能:

SELECT c1, SUM(c2) FROM t1 GROUP BY c1;

GROUP BY子句中的列不会形成索引的最左边的前缀:

SELECT c1, c2 FROM t1 GROUP BY c2, c3;

DISTINCT 优化

在大多数情况下,DISTINCT子句可以被认为是GROUP BY的特殊情况。例如,以下两个查询是等效的:

SELECT DISTINCT c1, c2, c3 FROM t1

WHERE c1 > const;

 

SELECT c1, c2, c3 FROM t1

WHERE c1 > const GROUP BY c1, c2, c3;

由于这种等效性,适用于GROUP BY查询的优化也可以应用于具有DISTINCT子句的查询。

 LIMIT 查询优化

结合order by和limit,如果order by上可以走索引,那么limit也可以很快返回。

避免全表扫描

当MySQL使用全表扫描来解析查询时,EXPLAIN的输出显示在类型列中的ALL。这通常发生在以下情况下:

  • 该表格非常小​​,执行表扫描速度比打扰查询更快。这对于具有少于10行和短行长度的表是常见的。
  • 索引列的ON或WHERE子句中没有可用的限制。
  • 您将索引列与常量值进行比较,MySQL已经计算(基于索引树)常量覆盖表的一部分太大,表扫描将更快。
  • 您正在使用低基数的列。在这种情况下,MySQL假定表扫描将更快。

对于小表,全表扫描通常是合适的,性能影响可以忽略不计。对于大表,请尝试以下技术,以避免优化器错误地选择表扫描:

  • 建立合适的索引。
  • 对于扫描表使用FORCE INDEX来告诉MySQL与使用给定索引相比,表扫描非常昂贵。

优化 Subqueries, Derived Tables

Subqueries:在 select 或 where 中包含的子查询

Derived Tables :FROM 子句的子查询

MySQL查询优化器具有不同的策略来评估子查询。 对于IN(或= ANY)子查询,优化器具有以下选择:

  • 半连接
  • 物化
  • EXISTS策略

对于NOT IN(或<> ALL)子查询,优化器具有以下选择:

  • 物化
  • EXISTS策略

对于派生表(FROM子句中的子查询),优化器具有以下选项:

  • 将派生表合并到外部查询块中
  • 将派生表实现为内部临时表

使用半连接变换优化子查询

在MySQL中,子查询必须满足这些标准才能作为半连接处理:

ps:半连接的限制很多,不需要每个都记住,如果你发现自己的查询很慢没有走半连接,可以查询手册看是否满足使用场景。

  • 它必须是出现在WHERE或ON子句顶层的IN(或= ANY)子查询,可能作为AND表达式中的术语。 例如:

SELECT ...

FROM ot1, ...

WHERE (oe1, ...) IN (SELECT ie1, ... FROM it1, ... WHERE ...);

  • 它必须是没有UNION结构的单个SELECT。
  • 它不能包含GROUP BY或HAVING子句。
  • 它不能被隐式分组(它不能包含聚合函数)。
  • 它不能有限制的ORDER BY。
  • STRAIGHT_JOIN修饰符不能存在。
  • 外表和内表的数量必须小于连接中允许的最大表数。

半连接的好处,半连接是两张表的连接,所以可能内表可以提出来作为NL的主表。

使用物化优化子查询

MySQL首次需要子查询结果,将该结果实现为临时表。 任何随后的结果都需要,MySQL再次指向临时表。

子查询实现可能时使用内存中临时表,如果表变得太大,则返回到磁盘存储。

如果不使用物化,则优化器有时将非相关子查询重写为相关子查询。 例如,以下IN子查询是不相关的(where_condition仅涉及从t2而不是t1的列):

SELECT * FROM t1

WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);

优化器可能会将其重写为EXISTS相关的子查询:

SELECT * FROM t1

WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);

使用临时表的子查询实现避免了这种重写,并且使得可以仅执行一次而不是每行外部查询一次执行子查询。

ps:mysql中,not in 和not exists对于null的处理和ORACLE是一样的。

使用EXISTS策略优化子查询

考虑以下子查询比较:

outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

如果使用非物化的in,那么对于outer的每一行,都要执行inner的sql一遍。所以考虑将上面的sql转换为:

EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)

转换之后,MySQL可以使用下推式相等来限制必须检查的行数来评估子查询。

 

优化 Derived Tables

mysql将延迟执行子查询直到它被使用。

考虑以下查询:

SELECT *

FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2

ON t1.f2=derived_t2.f1

WHERE t1.f1 > 0;

如果t1.f1 > 0不满足,那么子查询将不会执行。

对于派生表需要物化的情况,优化器可以向物化表添加索引,以加速对其的访问。 如果这样的索引能够引用访问表,则可以大大减少查询执行期间读取的数据量。 考虑以下查询:

SELECT *

FROM t1 JOIN (SELECT DISTINCT f1 FROM t2) AS derived_t2

ON t1.f1=derived_t2.f1;

优化器从derived_t2构造列f1上的索引。

原文地址:https://www.cnblogs.com/xiaolang8762400/p/7292745.html