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
值应具有下表所示的形式之一。
命令语法 | 含义 |
---|---|
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 的成本估算过于悲观。因此,还需要将mrr_cost_based
设置为off
才能使用 BKA。有关更多信息,请参阅第 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
(默认值on
)在 MySQL 8.4 中无效。请改用
block_nested_loop
标志。
有关更多信息,请参阅 第 10.2.1.4 节,“哈希连接优化”。
索引条件下推标志
index_condition_pushdown
(默认值on
)控制索引条件下推。
有关更多信息,请参阅 第 10.2.1.6 节,“索引条件下推优化”。
索引扩展标志
use_index_extensions
(默认值on
)控制索引扩展的使用。
有关更多信息,请参阅 第 10.3.10 节,“索引扩展的使用”。
索引合并标志
index_merge
(默认值on
)控制所有索引合并优化。
index_merge_intersection
(默认值on
)控制索引合并交集访问优化。
index_merge_sort_union
(默认值on
)控制索引合并排序并集访问优化。
index_merge_union
(默认值on
)控制索引合并并集访问优化。
有关更多信息,请参阅 第 10.2.1.3 节,“索引合并优化”。
索引可见性标志
use_invisible_indexes
(默认值off
)控制不可见索引的使用。
有关更多信息,请参阅 第 10.3.12 节,“不可见索引”。
限制优化标志
prefer_ordering_index
(默认值on
)控制在查询具有带有
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