10.9.2 可切换的优化
optimizer_switch
系统变量允许控制优化器行为。其值是一组标志,每个标志的值为 on
或 off
,表示相应的优化器行为是启用还是禁用。此变量具有全局和会话值,可以在运行时更改。全局默认值可以在服务器启动时设置。
要查看当前的优化器标志集,请选择变量值:
mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,
index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,
mrr=on,mrr_cost_based=on,block_nested_loop=on,
batched_key_access=off,materialization=on,semijoin=on,
loosescan=on,firstmatch=on,duplicateweedout=on,
subquery_materialization_cost_based=on,
use_index_extensions=on,condition_fanout_filter=on,
derived_merge=on,use_invisible_indexes=off,skip_scan=on,
hash_join=on,subquery_to_derived=off,
prefer_ordering_index=on,hypergraph_optimizer=off,
derived_condition_pushdown=on,hash_set_operations=on
1 row in set (0.00 sec)
要更改 optimizer_switch
的值,请分配一个由一个或多个命令组成的逗号分隔列表值:
SET [GLOBAL|SESSION] optimizer_switch='command[,command]...';
每个 command
值应具有下表所示的形式之一。
Command Syntax | Meaning |
---|---|
default |
将每个优化重置为其默认值 |
|
将命名优化设置为其默认值 |
|
禁用命名优化 |
|
启用命名优化 |
值中命令的顺序无关紧要,但如果存在 default
命令,则首先执行该命令。将 opt_name
标志设置为 default
会将其设置为 on
或 off
中的默认值。不允许在值中多次指定任何给定的 opt_name
,这会导致错误。值中的任何错误都会导致分配失败并出现错误,从而使 optimizer_switch
的值保持不变。
以下列表描述了允许的 opt_name
标志名称,按优化策略分组:
-
批处理键访问标志
-
batched_key_access
(默认off
)控制 BKA 连接算法的使用。
为了使
batched_key_access
在设置为on
时生效,mrr
标志也必须为on
。目前,MRR 的成本估算过于悲观。因此,为了使用 BKA,还需要将mrr_cost_based
设置为off
。有关更多信息,请参阅 第 10.2.1.12 节,“块嵌套循环和批处理键访问连接”。
-
-
块嵌套循环标志
-
block_nested_loop
(默认on
)
更多信息,请参见 第 10.2.1.12 节,“块嵌套循环和批量键访问连接”。
-
-
条件过滤标志
-
condition_fanout_filter
(默认on
)控制条件过滤的使用。
更多信息,请参见 第 10.2.1.13 节,“条件过滤”。
-
-
派生条件下推标志
-
derived_condition_pushdown
(默认on
)控制派生条件下推。
更多信息,请参见 第 10.2.2.5 节,“派生条件下推优化”
-
-
派生表合并标志
-
derived_merge
(默认on
)控制将派生表和视图合并到外部查询块中。
derived_merge
标志控制优化器是否尝试将派生表、视图引用和公用表表达式合并到外部查询块中,假设没有其他规则阻止合并;例如,视图的ALGORITHM
指令优先于derived_merge
设置。默认情况下,该标志为on
以启用合并。更多信息,请参见 第 10.2.2.4 节,“使用合并或物化优化派生表、视图引用和公用表表达式”。
-
-
引擎条件下推标志
-
engine_condition_pushdown
(默认on
)控制引擎条件下推。
更多信息,请参见 第 10.2.1.5 节,“引擎条件下推优化”。
-
-
哈希连接标志
-
hash_join
(默认启用
)在 MySQL 8.4 中无效。请改用
block_nested_loop
标志。
更多信息,请参阅 第 10.2.1.4 节 “哈希连接优化”。
-
-
索引条件下推标志
-
index_condition_pushdown
(默认启用
)控制索引条件下推。
更多信息,请参阅 第 10.2.1.6 节 “索引条件下推优化”。
-
-
索引扩展标志
-
use_index_extensions
(默认启用
)控制索引扩展的使用。
更多信息,请参阅 第 10.3.10 节 “索引扩展的使用”。
-
-
索引合并标志
-
index_merge
(默认启用
)控制所有索引合并优化。
-
index_merge_intersection
(默认启用
)控制索引合并交集访问优化。
-
index_merge_sort_union
(默认启用
)控制索引合并排序并集访问优化。
-
index_merge_union
(默认启用
)控制索引合并并集访问优化。
更多信息,请参阅 第 10.2.1.3 节 “索引合并优化”。
-
-
索引可见性标志
-
use_invisible_indexes
(默认禁用
)控制不可见索引的使用。
更多信息,请参阅 第 10.3.12 节 “不可见索引”。
-
-
限制优化标志
-
prefer_ordering_index
(默认启用
)控制在查询具有带有
LIMIT
子句的ORDER BY
或GROUP BY
的情况下,优化器是否尝试使用有序索引而不是无序索引、文件排序或其他优化。默认情况下,只要优化器确定使用此优化可以更快地执行查询,就会执行此优化。由于做出此决定的算法无法处理所有可能的情况(部分原因是假设数据的分布始终或多或少是均匀的),因此在某些情况下,此优化可能不可取。可以通过将
prefer_ordering_index
标志设置为off
来禁用此优化。
有关更多信息和示例,请参阅 第 10.2.1.19 节 “LIMIT 查询优化”。
-
-
多范围读取标志
-
mrr
(默认on
)控制多范围读取策略。
-
mrr_cost_based
(默认on
)如果
mrr=on
,则控制使用基于成本的 MRR。
有关更多信息,请参阅 第 10.2.1.11 节 “多范围读取优化”。
-
-
半连接标志
-
duplicateweedout
(默认on
)控制半连接重复数据消除策略。
-
firstmatch
(默认on
)控制半连接 FirstMatch 策略。
-
loosescan
(默认on
)控制半连接 LooseScan 策略(不要与
GROUP BY
的松散索引扫描混淆)。 -
semijoin
(默认on
)控制所有半连接策略。
这也适用于反连接优化。
semijoin
、firstmatch
、loosescan
和duplicateweedout
标志用于控制半连接策略。semijoin
标志控制是否使用半连接。如果设置为on
,则firstmatch
和loosescan
标志可以更精细地控制允许的半连接策略。如果禁用
duplicateweedout
半连接策略,除非所有其他适用的策略也被禁用,否则不会使用它。如果
semijoin
和materialization
都为on
,则半连接也会在适用时使用物化。默认情况下,这些标志为on
。更多信息,请参阅 使用半连接转换优化 IN 和 EXISTS 子查询谓词。
-
-
集合运算标志
-
hash_set_operations
(默认on
)启用涉及
EXCEPT
和INTERSECT
) 的集合运算的哈希表优化;默认启用。否则,将使用基于临时表的重复数据删除,如以前版本的 MySQL 中那样。可以使用
set_operations_buffer_size
系统变量控制此优化用于哈希的内存量;增加此值通常会导致使用这些操作的语句执行时间更快。
-
-
跳过扫描标志
-
skip_scan
(默认on
)控制跳过扫描访问方法的使用。
更多信息,请参阅 跳过扫描范围访问方法。
-
-
子查询物化标志
-
materialization
(默认on
)控制物化(包括半连接物化)。
-
subquery_materialization_cost_based
(默认on
)使用基于成本的物化选择。
materialization
标志控制是否使用子查询物化。如果semijoin
和materialization
都为on
,则半连接也会在适用时使用物化。默认情况下,这些标志为on
。subquery_materialization_cost_based
标志可以控制子查询物化和IN
到EXISTS
子查询转换之间的选择。如果该标志为on
(默认值),则优化器会在子查询物化和IN
到EXISTS
子查询转换之间执行基于成本的选择(如果可以使用这两种方法)。如果该标志为off
,则优化器选择子查询物化而不是IN
到EXISTS
子查询转换。更多信息,请参见 第 10.2.2 节,“优化子查询、派生表、视图引用和公用表表达式”。
-
-
子查询转换标志
-
subquery_to_derived
(默认off
)在许多情况下,优化器能够将
SELECT
、WHERE
、JOIN
或HAVING
子句中的标量子查询转换为派生表上的左外连接。(根据派生表的可空性,有时可以将其进一步简化为内连接。)这可以针对满足以下条件的子查询完成:此优化还可以应用于作为
IN
、NOT IN
、EXISTS
或NOT EXISTS
参数的表子查询,该子查询不包含GROUP BY
。此标志的默认值为
off
,因为在大多数情况下,启用此优化不会在性能上产生任何明显的改进(并且在许多情况下甚至会使查询运行更慢),但是您可以通过将subquery_to_derived
标志设置为on
来启用优化。它主要用于测试。示例,使用标量子查询:
d mysql> CREATE TABLE t1(a INT); mysql> CREATE TABLE t2(a INT); mysql> INSERT INTO t1 VALUES ROW(1), ROW(2), ROW(3), ROW(4); mysql> INSERT INTO t2 VALUES ROW(1), ROW(2); mysql> SELECT * FROM t1 -> WHERE t1.a > (SELECT COUNT(a) FROM t2); +------+ | a | +------+ | 3 | | 4 | +------+ mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=off%'; +-----------------------------------------------------+ | @@optimizer_switch LIKE '%subquery_to_derived=off%' | +-----------------------------------------------------+ | 1 | +-----------------------------------------------------+ mysql> EXPLAIN SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 4 filtered: 33.33 Extra: Using where *************************** 2. row *************************** id: 2 select_type: SUBQUERY table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: NULL mysql> SET @@optimizer_switch='subquery_to_derived=on'; mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=off%'; +-----------------------------------------------------+ | @@optimizer_switch LIKE '%subquery_to_derived=off%' | +-----------------------------------------------------+ | 0 | +-----------------------------------------------------+ mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=on%'; +----------------------------------------------------+ | @@optimizer_switch LIKE '%subquery_to_derived=on%' | +----------------------------------------------------+ | 1 | +----------------------------------------------------+ mysql> EXPLAIN SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 4 filtered: 33.33 Extra: Using where; Using join buffer (hash join) *************************** 3. row *************************** id: 2 select_type: DERIVED table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: NULL
从在第二个
EXPLAIN
语句之后立即执行SHOW WARNINGS
可以看出,启用优化后,查询SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2)
被重写为类似于此处所示的形式:SELECT t1.a FROM t1 JOIN ( SELECT COUNT(t2.a) AS c FROM t2 ) AS d WHERE t1.a > d.c;
示例,使用带有
IN (
的查询:子查询
)mysql> DROP TABLE IF EXISTS t1, t2; mysql> CREATE TABLE t1 (a INT, b INT); mysql> CREATE TABLE t2 (a INT, b INT); mysql> INSERT INTO t1 VALUES ROW(1,10), ROW(2,20), ROW(3,30); mysql> INSERT INTO t2 -> VALUES ROW(1,10), ROW(2,20), ROW(3,30), ROW(1,110), ROW(2,120), ROW(3,130); mysql> SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> t1.a IN (SELECT t2.a + 1 FROM t2); +------+------+ | a | b | +------+------+ | 2 | 20 | | 3 | 30 | +------+------+ mysql> SET @@optimizer_switch="subquery_to_derived=off"; mysql> EXPLAIN SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> t1.a IN (SELECT t2.a + 1 FROM t2)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 100.00 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 6 filtered: 100.00 Extra: Using where mysql> SET @@optimizer_switch="subquery_to_derived=on"; mysql> EXPLAIN SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> t1.a IN (SELECT t2.a + 1 FROM t2)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: ref possible_keys: <auto_key0> key: <auto_key0> key_len: 9 ref: std2.t1.a rows: 2 filtered: 100.00 Extra: Using where; Using index *************************** 3. row *************************** id: 2 select_type: DERIVED table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 6 filtered: 100.00 Extra: Using temporary
在对此查询执行
EXPLAIN
后检查和简化SHOW WARNINGS
的结果表明,当启用subquery_to_derived
标志时,SELECT * FROM t1 WHERE t1.b < 0 OR t1.a IN (SELECT t2.a + 1 FROM t2)
被重写为类似于此处所示的形式:SELECT a, b FROM t1 LEFT JOIN (SELECT DISTINCT a + 1 AS e FROM t2) d ON t1.a = d.e WHERE t1.b < 0 OR d.e IS NOT NULL;
示例,使用带有
EXISTS (
的查询以及与前一个示例相同的表和数据:子查询
)mysql> SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1); +------+------+ | a | b | +------+------+ | 1 | 10 | | 2 | 20 | +------+------+ mysql> SET @@optimizer_switch="subquery_to_derived=off"; mysql> EXPLAIN SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 100.00 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 6 filtered: 16.67 Extra: Using where mysql> SET @@optimizer_switch="subquery_to_derived=on"; mysql> EXPLAIN SELECT * FROM t1 -> WHERE t1.b < 0 -> OR -> EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 3 filtered: 100.00 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 6 filtered: 100.00 Extra: Using where; Using join buffer (hash join) *************************** 3. row *************************** id: 2 select_type: DERIVED table: t2 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 6 filtered: 100.00 Extra: Using temporary
如果我们在启用
subquery_to_derived
时,在查询SELECT * FROM t1 WHERE t1.b < 0 OR EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)
上运行EXPLAIN
后执行SHOW WARNINGS
,并简化结果的第二行,我们会看到它已被重写为类似于以下形式:SELECT a, b FROM t1 LEFT JOIN (SELECT DISTINCT 1 AS e1, t2.a AS e2 FROM t2) d ON t1.a + 1 = d.e2 WHERE t1.b < 0 OR d.e1 IS NOT NULL;
有关更多信息,请参阅 第 10.2.2.4 节,“使用合并或物化优化派生表、视图引用和公用表表达式”,以及 第 10.2.1.19 节,“LIMIT 查询优化”,和 使用半连接转换优化 IN 和 EXISTS 子查询谓词。
-
当你为optimizer_switch
分配值时,未提及的标志会保留其当前值。这使得可以在单个语句中启用或禁用特定的优化器行为,而不会影响其他行为。该语句不依赖于存在哪些其他优化器标志以及它们的值。假设所有索引合并优化都已启用:
mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,
index_merge_sort_union=on,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,
mrr=on,mrr_cost_based=on,block_nested_loop=on,
batched_key_access=off,materialization=on,semijoin=on,
loosescan=on, firstmatch=on,
subquery_materialization_cost_based=on,
use_index_extensions=on,condition_fanout_filter=on,
derived_merge=on,use_invisible_indexes=off,skip_scan=on,
hash_join=on,subquery_to_derived=off,
prefer_ordering_index=on
如果服务器对某些查询使用索引合并联合或索引合并排序联合访问方法,并且您想检查优化器是否可以在没有它们的情况下执行得更好,请像这样设置变量值:
mysql> SET optimizer_switch='index_merge_union=off,index_merge_sort_union=off';
mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=off,
index_merge_sort_union=off,index_merge_intersection=on,
engine_condition_pushdown=on,index_condition_pushdown=on,
mrr=on,mrr_cost_based=on,block_nested_loop=on,
batched_key_access=off,materialization=on,semijoin=on,
loosescan=on, firstmatch=on,
subquery_materialization_cost_based=on,
use_index_extensions=on,condition_fanout_filter=on,
derived_merge=on,use_invisible_indexes=off,skip_scan=on,
hash_join=on,subquery_to_derived=off,
prefer_ordering_index=on