控制优化器策略的一种方法是设置
optimizer_switch
系统变量(请参阅第 8.9.2 节,“可切换优化”)。对该变量的更改会影响所有后续查询的执行;要以不同方式影响一个查询,有必要
optimizer_switch
在每个查询之前进行更改。
另一种控制优化器的方法是使用优化器提示,它可以在单独的语句中指定。由于优化器提示基于每个语句应用,因此与使用
optimizer_switch
. 例如,您可以在一条语句中对一个表启用优化,而对另一个表禁用优化。语句中的提示优先于
optimizer_switch
标志。
例子:
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
FROM t3 WHERE f1 > 30 AND f1 < 33;
SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...;
EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...;
默认情况下,mysql客户端会从发送到服务器的 SQL 语句中删除注释(包括优化器提示),直到 MySQL 5.7.7 才更改为将优化器提示传递给服务器。如果您使用旧版本的mysql客户端和理解优化器提示的服务器版本,
要确保优化器提示不会被剥离,请使用
该
选项
调用mysql 。--comments
此处描述的优化器提示不同于第 8.9.4 节“索引提示”中描述的索引提示。优化器和索引提示可以单独使用,也可以一起使用。
优化器提示适用于不同的范围级别:
全局:提示影响整个语句
查询块:提示影响语句中的特定查询块
表级:提示影响查询块中的特定表
索引级别:提示影响表中的特定索引
下表总结了可用的优化器提示、它们影响的优化器策略以及它们应用的一个或多个范围。稍后给出更多细节。
表 8.2 可用的优化器提示
提示名称 | 描述 | 适用范围 |
---|---|---|
BKA ,
NO_BKA |
影响批处理密钥访问加入处理 | 查询块、表 |
BNL ,
NO_BNL |
影响块嵌套循环连接处理 | 查询块、表 |
MAX_EXECUTION_TIME |
限制语句执行时间 | 全球的 |
MRR ,
NO_MRR |
影响多范围读取优化 | 表、索引 |
NO_ICP |
影响索引条件下推优化 | 表、索引 |
NO_RANGE_OPTIMIZATION |
影响范围优化 | 表、索引 |
QB_NAME |
为查询块分配名称 | 查询块 |
SEMIJOIN ,
NO_SEMIJOIN |
半连接策略 | 查询块 |
SUBQUERY |
影响物化,
IN 到EXISTS
子查询策略 |
查询块 |
禁用优化会阻止优化器使用它。启用优化意味着优化器可以自由使用该策略(如果它适用于语句执行),而不是优化器必须使用它。
MySQL 支持 SQL 语句中的注释,如
第 9.6 节,“注释”中所述。必须在/*+ ... */
注释中指定优化器提示。也就是说,优化器提示使用/* ... */
C 风格注释语法的变体,在注释开始序列+
后有一个字符。/*
例子:
/*+ BKA(t1) */
/*+ BNL(t1, t2) */
/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */
/*+ QB_NAME(qb2) */
+
字符
后允许有空格。
SELECT
解析器在,
UPDATE
,
INSERT
,
REPLACE
, 和
DELETE
语句
的初始关键字之后识别优化器提示注释。在这些上下文中允许提示:
在查询和数据更改语句的开头:
SELECT /*+ ... */ ... INSERT /*+ ... */ ... REPLACE /*+ ... */ ... UPDATE /*+ ... */ ... DELETE /*+ ... */ ...
在查询块的开头:
(SELECT /*+ ... */ ... ) (SELECT ... ) UNION (SELECT /*+ ... */ ... ) (SELECT /*+ ... */ ... ) UNION (SELECT /*+ ... */ ... ) UPDATE ... WHERE x IN (SELECT /*+ ... */ ...) INSERT ... SELECT /*+ ... */ ...
在以
EXPLAIN
. 例如:EXPLAIN SELECT /*+ ... */ ... EXPLAIN UPDATE ... WHERE x IN (SELECT /*+ ... */ ...)
这意味着您可以使用它
EXPLAIN
来查看优化器提示如何影响执行计划。SHOW WARNINGS
之后立即使用EXPLAIN
以查看如何使用提示。以下显示的扩展EXPLAIN
输出SHOW WARNINGS
指示使用了哪些提示。不显示忽略的提示。
一个提示注释可以包含多个提示,但一个查询块不能包含多个提示注释。这是有效的:
SELECT /*+ BNL(t1) BKA(t2) */ ...
但这是无效的:
SELECT /*+ BNL(t1) */ /* BKA(t2) */ ...
当提示注释包含多个提示时,存在重复和冲突的可能性。以下一般准则适用。对于特定的提示类型,附加规则可能适用,如提示描述中所示。
重复提示:对于诸如 之类的提示
/*+ MRR(idx1) MRR(idx1) */
,MySQL 使用第一个提示并发出有关重复提示的警告。冲突提示:对于诸如 之类的提示
/*+ MRR(idx1) NO_MRR(idx1) */
,MySQL 使用第一个提示并发出关于第二个冲突提示的警告。
查询块名称是标识符,并遵循关于什么名称有效以及如何引用它们的通常规则(请参阅 第 9.2 节,“模式对象名称”)。
提示名称、查询块名称和策略名称不区分大小写。对表和索引名称的引用遵循通常的标识符区分大小写规则(请参阅 第 9.2.3 节,“标识符区分大小写”)。
表级提示会影响块嵌套循环 (BNL) 和批处理密钥访问 (BKA) 连接处理算法的使用(请参阅 第 8.2.1.11 节,“块嵌套循环和批处理密钥访问连接”)。这些提示类型适用于特定表,或查询块中的所有表。
表级提示的语法:
hint_name([@query_block_name] [tbl_name [, tbl_name] ...])
hint_name([tbl_name@query_block_name [, tbl_name@query_block_name] ...])
语法指的是这些术语:
hint_name
:允许使用这些提示名称:笔记要使用 BNL 或 BKA 提示为外连接的任何内表启用连接缓冲,必须为外连接的所有内表启用连接缓冲。
tbl_name
: 语句中使用的表的名称。该提示适用于它命名的所有表。如果提示未命名表,则它适用于出现它的查询块的所有表。如果表有别名,提示必须引用别名,而不是表名。
提示中的表名不能用模式名限定。
query_block_name
:提示适用的查询块。如果提示不包含 leading ,则提示适用于出现它的查询块。对于 语法,提示适用于命名查询块中的命名表。要为查询块分配名称,请参阅 命名查询块的优化器提示。@
query_block_name
tbl_name
@query_block_name
例子:
SELECT /*+ NO_BKA(t1, t2) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
SELECT /*+ NO_BNL() BKA(t1) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
表级提示适用于从以前的表中接收记录的表,而不适用于发送方表。考虑这个声明:
SELECT /*+ BNL(t2) */ FROM t1, t2;
如果优化器选择首先处理,它会在开始读取之前
通过缓冲来自的行来
t1
应用块嵌套循环连接
。如果优化器选择首先处理,则提示无效,因为它是一个发送方表。
t2
t1
t2
t2
t2
索引级提示会影响优化器对特定表或索引使用的索引处理策略。这些提示类型会影响索引条件下推 (ICP)、多范围读取 (MRR) 和范围优化的使用(请参阅 第 8.2.1 节,“优化 SELECT 语句”)。
索引级提示的语法:
hint_name([@query_block_name] tbl_name [index_name [, index_name] ...])
hint_name(tbl_name@query_block_name [index_name [, index_name] ...])
语法指的是这些术语:
hint_name
:允许使用这些提示名称:NO_ICP
: 禁用指定表或索引的ICP。默认情况下,ICP 是候选优化策略,因此没有启用它的提示。NO_RANGE_OPTIMIZATION
: 禁用指定表或索引的索引范围访问。此提示还为表或索引禁用索引合并和松散索引扫描。默认情况下,范围访问是一种候选优化策略,因此没有启用它的提示。当范围数量可能很多并且范围优化需要很多资源时,此提示可能很有用。
tbl_name
:提示适用的表。index_name
: 命名表中索引的名称。该提示适用于它命名的所有索引。如果提示没有命名索引,它适用于表中的所有索引。To refer to a primary key, use the name
PRIMARY
. To see the index names for a table, useSHOW INDEX
.query_block_name
: The query block to which the hint applies. If the hint includes no leading@
, the hint applies to the query block in which it occurs. Forquery_block_name
syntax, the hint applies to the named table in the named query block. To assign a name to a query block, see Optimizer Hints for Naming Query Blocks.tbl_name
@query_block_name
Examples:
SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3;
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
FROM t3 WHERE f1 > 30 AND f1 < 33;
INSERT INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2
WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1
AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1);
Subquery hints affect whether to use semijoin transformations
and which semijoin strategies to permit, and, when semijoins
are not used, whether to use subquery materialization or
IN
-to-EXISTS
transformations. For more information about these
optimizations, see Section 8.2.2, “Optimizing Subqueries, Derived Tables, and View References”.
Syntax of hints that affect semijoin strategies:
hint_name([@query_block_name] [strategy [, strategy] ...])
The syntax refers to these terms:
hint_name
: These hint names are permitted:SEMIJOIN
,NO_SEMIJOIN
: Enable or disable the named semijoin strategies.
strategy
: A semijoin strategy to be enabled or disabled. These strategy names are permitted:DUPSWEEDOUT
,FIRSTMATCH
,LOOSESCAN
,MATERIALIZATION
.For
SEMIJOIN
hints, if no strategies are named, semijoin is used if possible based on the strategies enabled according to theoptimizer_switch
system variable. If strategies are named but inapplicable for the statement,DUPSWEEDOUT
is used.For
NO_SEMIJOIN
hints, if no strategies are named, semijoin is not used. If strategies are named that rule out all applicable strategies for the statement,DUPSWEEDOUT
is used.
If one subquery is nested within another and both are merged
into a semijoin of an outer query, any specification of
semijoin strategies for the innermost query are ignored.
SEMIJOIN
and
NO_SEMIJOIN
hints can still
be used to enable or disable semijoin transformations for such
nested subqueries.
If DUPSWEEDOUT
is disabled, on occasion the
optimizer may generate a query plan that is far from optimal.
This occurs due to heuristic pruning during greedy search,
which can be avoided by setting
optimizer_prune_level=0
.
Examples:
SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) */ * FROM t2
WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);
SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2
WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);
Syntax of hints that affect whether to use subquery
materialization or
IN
-to-EXISTS
transformations:
SUBQUERY([@query_block_name] strategy)
The hint name is always
SUBQUERY
.
For SUBQUERY
hints, these
strategy
values are permitted:
INTOEXISTS
,
MATERIALIZATION
.
Examples:
SELECT id, a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1) FROM t2;
SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) */ a FROM t1);
For semijoin and SUBQUERY
hints, a leading
@
specifies the query block to which the hint applies. If the
hint includes no leading
query_block_name
@
,
the hint applies to the query block in which it occurs. To
assign a name to a query block, see
Optimizer Hints for Naming Query Blocks.
query_block_name
If a hint comment contains multiple subquery hints, the first is used. If there are other following hints of that type, they produce a warning. Following hints of other types are silently ignored.
The MAX_EXECUTION_TIME
hint
is permitted only for SELECT
statements. It places a limit N
(a
timeout value in milliseconds) on how long a statement is
permitted to execute before the server terminates it:
MAX_EXECUTION_TIME(N)
Example with a timeout of 1 second (1000 milliseconds):
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 WHERE ...
The
MAX_EXECUTION_TIME(
hint sets a statement execution timeout of
N
)N
milliseconds. If this option is
absent or N
is 0, the statement
timeout established by the
max_execution_time
system
variable applies.
The MAX_EXECUTION_TIME
hint
is applicable as follows:
For statements with multiple
SELECT
keywords, such as unions or statements with subqueries,MAX_EXECUTION_TIME
applies to the entire statement and must appear after the firstSELECT
.It applies to read-only
SELECT
statements. Statements that are not read only are those that invoke a stored function that modifies data as a side effect.It does not apply to
SELECT
statements in stored programs and is ignored.
Table-level, index-level, and subquery optimizer hints permit
specific query blocks to be named as part of their argument
syntax. To create these names, use the
QB_NAME
hint, which assigns
a name to the query block in which it occurs:
QB_NAME(name)
QB_NAME
hints can be used to
make explicit in a clear way which query blocks other hints
apply to. They also permit all non-query block name hints to
be specified within a single hint comment for easier
understanding of complex statements. Consider the following
statement:
SELECT ...
FROM (SELECT ...
FROM (SELECT ... FROM ...)) ...
QB_NAME
hints assign names
to query blocks in the statement:
SELECT /*+ QB_NAME(qb1) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...
Then other hints can use those names to refer to the appropriate query blocks:
SELECT /*+ QB_NAME(qb1) MRR(@qb1 t1) BKA(@qb2) NO_MRR(@qb3t1 idx1, id2) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...
The resulting effect is as follows:
MRR(@qb1 t1)
applies to tablet1
in query blockqb1
.BKA(@qb2)
applies to query blockqb2
.NO_MRR(@qb3 t1 idx1, id2)
适用 于查询块中的索引idx1
和idx2
表。t1
qb3
查询块名称是标识符,并遵循关于什么名称有效以及如何引用它们的通常规则(请参阅 第 9.2 节,“模式对象名称”)。例如,包含空格的查询块名称必须用引号引起来,这可以使用反引号来完成:
SELECT /*+ BKA(@`my hint name`) */ ...
FROM (SELECT /*+ QB_NAME(`my hint name`) */ ...) ...
如果ANSI_QUOTES
启用了 SQL 模式,也可以在双引号内引用查询块名称:
SELECT /*+ BKA(@"my hint name") */ ...
FROM (SELECT /*+ QB_NAME("my hint name") */ ...) ...