10.9.3 Подсказки оптимизатору
Один из способов управления стратегиями оптимизатора — это установка системной переменной optimizer_switch (см. Раздел 10.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 ...;
SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
Подсказки оптимизатора, описанные здесь, отличаются от подсказок индексов, описанных в Разделе 10.9.4, «Подсказки индексов». Подсказки оптимизатора и индексов могут использоваться по отдельности или вместе.
Обзор подсказок оптимизатору
Подсказки оптимизатору применяются на разных уровнях:
Глобальный: Подсказка влияет на всё выражение.
Блок запроса: Подсказка влияет на конкретный блок запроса в выражении.
Уровень таблицы: Подсказка влияет на конкретную таблицу в блоке запроса.
Уровень индекса: Подсказка влияет на конкретный индекс в таблице.
В следующей таблице обобщены доступные подсказки оптимизатору, стратегии оптимизации, на которые они влияют, и области применения. Более подробная информация приведена ниже.
Таблица 10.2 Доступные подсказки оптимизатору
| Имя подсказки | Описание | Применимые области |
|---|---|---|
BKA, NO_BKA
| Влияет на обработку соединений с объединением по ключу в пакетном режиме | Блок запроса, таблица |
BNL, NO_BNL
| Влияет на оптимизацию соединения с хэш-соединением | Блок запроса, таблица |
DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN
| Использовать или игнорировать оптимизацию перемещения производных условий для материализованных производных таблиц | Блок запроса, таблица |
GROUP_INDEX, NO_GROUP_INDEX
| Использовать или игнорировать указанный индекс или индексы для сканирования индексов в операциях GROUP BY | Индекс |
HASH_JOIN, NO_HASH_JOIN
| Влияет на оптимизацию хэш-соединения (нет эффекта в MySQL 8.4) | Блок запроса, таблица |
INDEX, NO_INDEX
| Действует как комбинация JOIN_INDEX, GROUP_INDEX и ORDER_INDEX, или как комбинация NO_JOIN_INDEX, NO_GROUP_INDEX и NO_ORDER_INDEX | Индекс |
INDEX_MERGE, NO_INDEX_MERGE
| Влияет на оптимизацию слияния индексов | Таблица, индекс |
JOIN_FIXED_ORDER | Использовать порядок таблиц, указанный в предложении FROM, для порядка соединения | Блок запроса |
JOIN_INDEX, NO_JOIN_INDEX
| Использовать или игнорировать указанный индекс или индексы для любого метода доступа | Индекс |
JOIN_ORDER | Использовать порядок таблиц, указанный в подсказке, для порядка соединения | Блок запроса |
JOIN_PREFIX | Использовать порядок таблиц, указанный в подсказке, для первых таблиц в порядке соединения | Блок запроса |
JOIN_SUFFIX | Использовать порядок таблиц, указанный в подсказке, для последних таблиц в порядке соединения | Блок запроса |
MAX_EXECUTION_TIME | Ограничение времени выполнения запроса | Глобальный |
MERGE, NO_MERGE
| Влияет на слияние производных таблиц/представлений в внешний блок запроса | Таблица |
MRR, NO_MRR
| Влияет на оптимизацию многодиапазонного чтения | Таблица, индекс |
NO_ICP | Влияет на оптимизацию перемещения условий по индексу | Таблица, индекс |
NO_RANGE_OPTIMIZATION | Влияет на оптимизацию диапазонов | Таблица, индекс |
ORDER_INDEX, NO_ORDER_INDEX
| Использовать или игнорировать указанный индекс или индексы для сортировки строк | Индекс |
QB_NAME | Назначает имя блоку запроса | Блок запроса |
RESOURCE_GROUP | Устанавливает группу ресурсов во время выполнения запроса | Глобальный |
SEMIJOIN, NO_SEMIJOIN
| Влияет на стратегии полусоединения и антисоединения | Блок запроса |
SKIP_SCAN, NO_SKIP_SCAN
| Влияет на оптимизацию пропусков сканирования | Таблица, индекс |
SET_VAR | Установка переменной во время выполнения запроса | Глобальный |
SUBQUERY | Влияет на материализацию, стратегии подзапросов от IN к EXISTS | Блок запроса |
Отключение оптимизации не позволяет оптимизатору её использовать. Включение оптимизации означает, что оптимизатор свободен использовать стратегию, если она применима к выполнению запроса, а не что оптимизатор обязательно её использует.
Синтаксис подсказок оптимизатора
MySQL поддерживает комментарии в операторах SQL, как описано в разделе 11.7, «Комментарии». Подсказки оптимизатора должны быть указаны внутри /*+ ... */ комментариев. То есть, подсказки оптимизатора используют вариант синтаксиса комментариев /* ... */ 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 использует первую подсказку и выводит предупреждение о второй конфликтующей подсказке.
Имена блоков запросов являются идентификаторами и следуют обычным правилам относительно того, какие имена допустимы и как их заключать в кавычки (см. раздел 11.2, «Имена объектов схемы»).
Имена подсказок, имена блоков запросов и имена стратегий нечувствительны к регистру. Ссылки на имена таблиц и индексов следуют обычным правилам чувствительности к регистру идентификаторов (см. раздел 11.2.3, «Чувствительность к регистру идентификаторов»).
Указания оптимизатора для порядка соединения
Указания для порядка соединения влияют на порядок, в котором оптимизатор соединяет таблицы.
Синтаксис указания JOIN_FIXED_ORDER:
hint_name([@query_block_name])
Синтаксис других указаний порядка соединения:
hint_name([@query_block_name] tbl_name [, tbl_name] ...)
hint_name(tbl_name[@query_block_name] [, tbl_name[@query_block_name]] ...)
Синтаксис ссылается на следующие термины:
-
hint_name: Разрешены следующие имена указаний:JOIN_FIXED_ORDER: Принуждает оптимизатор соединять таблицы в порядке их появления в предложенииFROM. Это эквивалентно указаниюSELECT STRAIGHT_JOIN.JOIN_ORDER: Указывает оптимизатору порядок соединения таблиц. Указание относится к указанным таблицам. Оптимизатор может поместить таблицы, не указанные в списке, в любой части порядка соединения, в том числе между указанными таблицами.JOIN_PREFIX: Указывает оптимизатору порядок соединения таблиц для первых таблиц в плане выполнения соединения. Указание относится к указанным таблицам. Оптимизатор размещает все другие таблицы после указанных.JOIN_SUFFIX: Указывает оптимизатору порядок соединения таблиц для последних таблиц в плане выполнения соединения. Указание относится к указанным таблицам. Оптимизатор размещает все другие таблицы перед указанными.
-
tbl_name: Имя таблицы, используемой в запросе. Указание, содержащее имена таблиц, относится ко всем указанным таблицам. УказаниеJOIN_FIXED_ORDERне указывает таблицы и относится ко всем таблицам в предложенииFROMблока запроса, в котором оно находится.Если у таблицы есть псевдоним, указания должны ссылаться на псевдоним, а не на имя таблицы.
Имена таблиц в указаниях не могут быть квалифицированы именами схем.
query_block_name: Блок запроса, к которому относится указание. Если указание не содержит ведущего@, то указание относится к блоку запроса, в котором оно находится. Для синтаксисаquery_block_nameуказание относится к указанной таблице в указанном блоке запроса. Для присвоения имени блоку запроса см. Указания оптимизатора для именования блоков запросов.tbl_name@query_block_name
Пример:
SELECT
/*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1)
JOIN_ORDER(t4@subq1, t3)
JOIN_SUFFIX(t1) */
COUNT(*) FROM t1 JOIN t2 JOIN t3
WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4)
AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5);
Указания управляют поведением таблиц полусоединения, которые объединяются с внешним блоком запроса. Если подзапросы subq1 и subq2 преобразуются в полусоединения, таблицы t4@subq1 и t5@subq2 объединяются с внешним блоком запроса. В этом случае указание, заданное во внешнем блоке запроса, управляет поведением таблиц t4@subq1, t5@subq2.
Оптимизатор разрешает указания порядка соединения в соответствии со следующими принципами:
-
Несколько экземпляров указаний
Применяется только одно указание
JOIN_PREFIXиJOIN_SUFFIXкаждого типа. Любые последующие указания того же типа игнорируются с предупреждением.JOIN_ORDERможет быть указано несколько раз.Примеры:
/*+ JOIN_PREFIX(t1) JOIN_PREFIX(t2) */
Второе указание
JOIN_PREFIXигнорируется с предупреждением./*+ JOIN_PREFIX(t1) JOIN_SUFFIX(t2) */
Оба указания применяются. Предупреждение не возникает.
/*+ JOIN_ORDER(t1, t2) JOIN_ORDER(t2, t3) */
Оба указания применяются. Предупреждение не возникает.
-
Противоречивые указания
В некоторых случаях указания могут противоречить друг другу, например, когда
JOIN_ORDERиJOIN_PREFIXимеют порядки таблиц, которые невозможно применить одновременно:SELECT /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */ ... FROM t1, t2;
В этом случае применяется первое указание, а последующие противоречивые указания игнорируются без предупреждения. Действительное указание, которое невозможно применить, молча игнорируется без предупреждения.
-
Игнорируемые указания
Указание игнорируется, если таблица, указанная в указании, имеет циклическую зависимость.
Пример:
/*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */
Указание
JOIN_ORDERустанавливает зависимость таблицыt2от таблицыt1. УказаниеJOIN_PREFIXигнорируется, поскольку таблицаt1не может зависеть от таблицыt2. Игнорируемые указания не отображаются в расширенном выводеEXPLAIN. -
Взаимодействие с таблицами
constОптимизатор MySQL помещает таблицы
constна первое место в порядке соединения, а позицию таблицыconstнельзя изменить с помощью указаний. Ссылки на таблицыconstв указаниях порядка соединения игнорируются, хотя само указание все еще применимо. Например, эти записи эквивалентны:JOIN_ORDER(t1,
const_tbl, t2) JOIN_ORDER(t1, t2)Принятые указания, показанные в расширенном выводе
EXPLAIN, включают таблицыconstтак, как они были указаны. -
Взаимодействие с типами операций соединения
MySQL поддерживает несколько типов соединений:
LEFT,RIGHT,INNER,CROSS,STRAIGHT_JOIN. Указание, противоречащее указанному типу соединения, игнорируется без предупреждения.Пример:
SELECT /*+ JOIN_PREFIX(t1, t2) */FROM t2 LEFT JOIN t1;
Здесь возникает конфликт между требуемым порядком соединения в указании и порядком, необходимым для
LEFT JOIN. Указание игнорируется без предупреждения.
Указатели оптимизатора на уровне таблиц
Указатели на уровне таблиц влияют на:
Использование алгоритмов обработки объединений Блок вложенного цикла (BNL) и Партионного доступа по ключу (BKA) (см. Раздел 10.2.1.12, «Block Nested-Loop и Batched Key Access Joins»).
То, будут ли производные таблицы, ссылки на представления или выражения общих таблиц сливаться в внешний блок запроса или материализоваться с использованием внутренней временной таблицы.
Использование оптимизации переноса условия производной таблицы. См. Раздел 10.2.2.5, «Оптимизация переноса условия производной таблицы».
Эти типы указателей применяются к определённым таблицам или ко всем таблицам в блоке запроса.
Синтаксис указателей на уровне таблиц:
hint_name([@query_block_name] [tbl_name [, tbl_name] ...])
hint_name([tbl_name@query_block_name [, tbl_name@query_block_name] ...])
Синтаксис относится к этим терминам:
-
hint_name: Разрешены следующие имена указателей:BKA,NO_BKA: Включить или отключить партионный доступ по ключу для указанных таблиц.BNL,NO_BNL: Включить и отключить оптимизацию объединения по хешу.DERIVED_CONDITION_PUSHDOWN,NO_DERIVED_CONDITION_PUSHDOWN: Включить или отключить использование переноса условия производной таблицы для указанных таблиц. Дополнительную информацию см. в Разделе 10.2.2.5, «Оптимизация переноса условия производной таблицы».HASH_JOIN,NO_HASH_JOIN: Эти указатели не имеют эффекта в MySQL 8.4; используйтеBNLилиNO_BNLвместо них.-
MERGE,NO_MERGE: Включить объединение для указанных таблиц, ссылок на представления или выражений общих таблиц; или отключить объединение и использовать материализацию вместо него.
ПримечаниеЧтобы использовать указатель «блок вложенного цикла» или «партионный доступ по ключу» для включения буферизации объединения для любой внутренней таблицы внешнего объединения, буферизация объединения должна быть включена для всех внутренних таблиц внешнего объединения.
-
tbl_name: Имя таблицы, используемой в инструкции. Указатель применяется ко всем таблицам, которые он называет. Если указатель не называет таблиц, он применяется ко всем таблицам блока запроса, в котором он встречается.Если у таблицы есть псевдоним, указатели должны ссылаться на псевдоним, а не на имя таблицы.
Имена таблиц в указателях не могут быть квалифицированы именами схем.
query_block_name: Блок запроса, к которому применяется указатель. Если указатель не включает ведущий@, указатель применяется к блоку запроса, в котором он встречается. Для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 /*+ NO_MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
Указатель на уровне таблиц применяется к таблицам, которые получают записи из предыдущих таблиц, а не к таблицам-отправителям. Рассмотрим следующее утверждение:
SELECT /*+ BNL(t2) */ FROM t1, t2;
Если оптимизатор выбирает обработку t1 первой, он применяет объединение «Блок вложенного цикла» к t2, буферизируя строки из t1 перед началом чтения из t2. Если оптимизатор вместо этого выбирает обработку t2 первой, указатель не оказывает никакого влияния, потому что t2 является таблицей-отправителем.
Для указателей MERGE и NO_MERGE применяются следующие правила приоритета:
Указатель имеет приоритет над любым эвристическим правилом оптимизатора, которое не является техническим ограничением. (Если предоставление указателя в качестве предложения не оказывает никакого эффекта, у оптимизатора есть причина для его игнорирования.)
Указатель имеет приоритет над флагом
derived_mergeпеременной системыoptimizer_switch.Для ссылок на представления, условие
ALGORITHM={MERGE|TEMPTABLE}в определении представления имеет приоритет над указателем, указанным в запросе, ссылающемся на представление.
Подсказки оптимизатора на уровне индексов
Подсказки на уровне индексов влияют на стратегии обработки индексов, используемые оптимизатором для конкретных таблиц или индексов. Эти типы подсказок влияют на использование Index Condition Pushdown (ICP), Multi-Range Read (MRR), слияние индексов и оптимизации диапазонов (см. Раздел 10.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: Разрешены следующие имена подсказок:GROUP_INDEX,NO_GROUP_INDEX: Включают или отключают указанный индекс или индексы для сканирования индексов для операцийGROUP BY. Эквивалентно подсказкам индексаFORCE INDEX FOR GROUP BY,IGNORE INDEX FOR GROUP BY.INDEX,NO_INDEX: Действуют как комбинацияJOIN_INDEX,GROUP_INDEXиORDER_INDEX, принуждая сервер использовать указанный индекс или индексы для всех областей; или как комбинацияNO_JOIN_INDEX,NO_GROUP_INDEXиNO_ORDER_INDEX, заставляя сервер игнорировать указанный индекс или индексы для всех областей. ЭквивалентноFORCE INDEX,IGNORE INDEX.-
INDEX_MERGE,NO_INDEX_MERGE: Включают или отключают метод доступа Index Merge для указанной таблицы или индексов. Дополнительную информацию об этом методе доступа см. в Разделе 10.2.1.3, «Оптимизация слияния индексов». Эти подсказки применяются ко всем трём алгоритмам слияния индексов.Подсказка
INDEX_MERGEпринуждает оптимизатор использовать Index Merge для указанной таблицы с использованием указанного набора индексов. Если индекс не указан, оптимизатор рассматривает все возможные комбинации индексов и выбирает наименее затратный вариант. Подсказка может быть проигнорирована, если комбинация индексов неприменима к данному запросу.Подсказка
NO_INDEX_MERGEотключает комбинации Index Merge, которые включают любой из указанных индексов. Если в подсказке не указаны индексы, Index Merge не разрешён для таблицы. JOIN_INDEX,NO_JOIN_INDEX: Принуждает MySQL использовать или игнорировать указанный индекс или индексы для любого метода доступа, такого какref,range,index_mergeи так далее. ЭквивалентноFORCE INDEX FOR JOIN,IGNORE INDEX FOR JOIN.MRR,NO_MRR: Включают или отключают MRR для указанной таблицы или индексов. Подсказки MRR применяются только к таблицамInnoDBиMyISAM. Дополнительную информацию об этом методе доступа см. в Разделе 10.2.1.11, «Оптимизация Multi-Range Read».NO_ICP: Отключает ICP для указанной таблицы или индексов. По умолчанию ICP является потенциальной стратегией оптимизации, поэтому для её включения нет подсказки. Дополнительную информацию об этом методе доступа см. в Разделе 10.2.1.6, «Оптимизация Index Condition Pushdown».-
NO_RANGE_OPTIMIZATION: Отключает доступ к индексу по диапазонам для указанной таблицы или индексов. Эта подсказка также отключает слияние индексов и свободное сканирование индексов для таблицы или индексов. По умолчанию доступ по диапазонам является потенциальной стратегией оптимизации, поэтому для её включения нет подсказки.Эта подсказка может быть полезной, когда количество диапазонов может быть высоким, и оптимизация по диапазонам потребует значительных ресурсов.
ORDER_INDEX,NO_ORDER_INDEX: Принуждает MySQL использовать или игнорировать указанный индекс или индексы для сортировки строк. ЭквивалентноFORCE INDEX FOR ORDER BY,IGNORE INDEX FOR ORDER BY.-
SKIP_SCAN,NO_SKIP_SCAN: Включают или отключают метод доступа Skip Scan для указанной таблицы или индексов. Дополнительную информацию об этом методе доступа см. в Методе доступа Skip Scan по диапазонам.Подсказка
SKIP_SCANпринуждает оптимизатор использовать Skip Scan для указанной таблицы с использованием указанного набора индексов. Если индекс не указан, оптимизатор рассматривает все возможные индексы и выбирает наименее затратный вариант. Подсказка может быть проигнорирована, если индекс неприменим к данному запросу.Подсказка
NO_SKIP_SCANотключает Skip Scan для указанных индексов. Если в подсказке не указаны индексы, Skip Scan не разрешён для таблицы.
tbl_name: Таблица, к которой применяется подсказка.-
index_name: Имя индекса в указанной таблице. Подсказка применяется ко всем индексам, которые она называет. Если подсказка не называет индексы, она применяется ко всем индексам в таблице.Для ссылки на первичный ключ используйте имя
PRIMARY. Чтобы увидеть имена индексов для таблицы, используйтеSHOW INDEX. query_block_name: Блок запроса, к которому применяется подсказка. Если подсказка не включает ведущий@, подсказка применяется к блоку запроса, в котором она расположена. Для синтаксисаquery_block_nameподсказка применяется к указанной таблице в указанном блоке запроса. Чтобы присвоить имя блоку запроса, см. Подсказки оптимизатора для именования блоков запросов.tbl_name@query_block_name
Примеры:
SELECT /*+ INDEX_MERGE(t1 f3, PRIMARY) */ f2 FROM t1
WHERE f1 = 'o' AND f2 = f3 AND f3 <= 4;
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);
SELECT /*+ SKIP_SCAN(t1 PRIMARY) */ f1, f2
FROM t1 WHERE f2 > 40;
В следующих примерах используются подсказки Index Merge, но и другие подсказки на уровне индексов следуют тем же принципам по поводу игнорирования подсказок и приоритета подсказок оптимизатора по отношению к системной переменной optimizer_switch или подсказкам индекса.
Предположим, что таблица t1 имеет столбцы a, b, c и d; и что индексы с именами i_a, i_b и i_c существуют на a, b и c соответственно:
SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
WHERE a = 1 AND b = 2 AND c = 3 AND d = 4;
В этом случае используется слияние индексов для (i_a, i_b, i_c).
SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
WHERE b = 1 AND c = 2 AND d = 3;
В этом случае используется слияние индексов для (i_b, i_c).
/*+ INDEX_MERGE(t1 i_a, i_b) NO_INDEX_MERGE(t1 i_b) */
NO_INDEX_MERGE игнорируется, потому что для той же таблицы есть предыдущая подсказка.
/*+ NO_INDEX_MERGE(t1 i_a, i_b) INDEX_MERGE(t1 i_b) */
INDEX_MERGE игнорируется, потому что для той же таблицы есть предыдущая подсказка.
Для подсказок оптимизатора INDEX_MERGE и NO_INDEX_MERGE применяются следующие правила приоритета:
-
Если указано подсказка оптимизатора и она применима, она имеет приоритет над флагами
optimizer_switchпеременной системной, связанными с Объединением индексов.SET optimizer_switch='index_merge_intersection=off'; SELECT /*+ INDEX_MERGE(t1 i_b, i_c) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
Подсказка имеет приоритет над
optimizer_switch. В этом случае используется Объединение индексов для(i_b, i_c).SET optimizer_switch='index_merge_intersection=on'; SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
Подсказка указывает только один индекс, поэтому она неприменима, и применяется флаг
optimizer_switch(on). Объединение индексов используется, если оптимизатор считает это целесообразным с точки зрения затрат.SET optimizer_switch='index_merge_intersection=off'; SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
Подсказка указывает только один индекс, поэтому она неприменима, и применяется флаг
optimizer_switch(off). Объединение индексов не используется. -
Подсказки оптимизатора уровня индекса
GROUP_INDEX,INDEX,JOIN_INDEXиORDER_INDEXимеют приоритет над эквивалентными подсказкамиFORCE INDEX; то есть, они приводят к игнорированию подсказокFORCE INDEX. Аналогично, подсказкиNO_GROUP_INDEX,NO_INDEX,NO_JOIN_INDEXиNO_ORDER_INDEXимеют приоритет над любыми эквивалентамиIGNORE INDEX, что также приводит к их игнорированию.Подсказки оптимизатора уровня индекса
GROUP_INDEX,NO_GROUP_INDEX,INDEX,NO_INDEX,JOIN_INDEX,NO_JOIN_INDEX,ORDER_INDEXиNO_ORDER_INDEXимеют приоритет над всеми другими подсказками оптимизатора, включая другие подсказки оптимизатора уровня индекса. Любые другие подсказки оптимизатора применяются только к разрешенным этими индексам.Подсказки
GROUP_INDEX,INDEX,JOIN_INDEXиORDER_INDEXэквивалентны подсказкеFORCE INDEX, а не подсказкеUSE INDEX. Это потому, что использование одной или нескольких из этих подсказок означает, что сканирование таблицы используется только в том случае, если нет возможности найти строки в таблице с помощью одного из указанных индексов. Чтобы заставить MySQL использовать тот же индекс или набор индексов, что и с данным экземпляромUSE INDEX, можно использоватьNO_INDEX,NO_JOIN_INDEX,NO_GROUP_INDEX,NO_ORDER_INDEXили их комбинацию.Чтобы воспроизвести эффект, который имеет
USE INDEXв запросеSELECT a,c FROM t1 USE INDEX FOR ORDER BY (i_a) ORDER BY a, можно использовать подсказку оптимизатораNO_ORDER_INDEXдля покрытия всех индексов в таблице, кроме желаемого, вот так:SELECT /*+ NO_ORDER_INDEX(t1 i_b,i_c) */ a,c FROM t1 ORDER BY a;Попытка объединить
NO_ORDER_INDEXдля всей таблицы сUSE INDEX FOR ORDER BYне работает для этого, потому чтоNO_ORDER_BYприводит к игнорированиюUSE INDEX, как показано здесь:mysql>
EXPLAIN SELECT /*+ NO_ORDER_INDEX(t1) */ a,c FROM t1->USE INDEX FOR ORDER BY (i_a) ORDER BY a\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 256 filtered: 100.00 Extra: Using filesort -
Подсказки индексов
USE INDEX,FORCE INDEXиIGNORE INDEXимеют более высокий приоритет, чем подсказки оптимизатораINDEX_MERGEиNO_INDEX_MERGE./*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ ... IGNORE INDEX i_a
IGNORE INDEXимеет приоритет надINDEX_MERGE, поэтому индексi_aисключается из возможных диапазонов для Объединения индексов./*+ NO_INDEX_MERGE(t1 i_a, i_b) */ ... FORCE INDEX i_a, i_b
Объединение индексов запрещено для
i_a, i_bиз-заFORCE INDEX, но оптимизатор вынужден использовать либоi_a, либоi_bдля доступаrangeилиref. Конфликтов нет; обе подсказки применимы. Если подсказка
IGNORE INDEXуказывает несколько индексов, эти индексы недоступны для Объединения индексов.-
Подсказки
FORCE INDEXиUSE INDEXделают доступными для Объединения индексов только указанные индексы.SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ a FROM t1 FORCE INDEX (i_a, i_b) WHERE c = 'h' AND a = 2 AND b = 'b';
Алгоритм пересечения доступа Объединения индексов используется для
(i_a, i_b). То же самое верно, еслиFORCE INDEXизменено наUSE INDEX.
Подсказки оптимизатора для подзапросов
Подсказки подзапросов влияют на использование преобразований полусоединений и на разрешаемые стратегии полусоединений, а также, когда полусоединения не используются, на использование материализации подзапроса или преобразования IN в EXISTS. Для получения дополнительной информации об этих оптимизациях см. Раздел 10.2.2, «Оптимизация подзапросов, производных таблиц, ссылок на представления и общих табличных выражений».
Синтаксис подсказок, влияющих на стратегии полусоединений:
hint_name([@query_block_name] [strategy [, strategy] ...])
Синтаксис относится к этим терминам:
-
hint_name: Разрешены следующие имена подсказок:SEMIJOIN,NO_SEMIJOIN: Включение или выключение указанных стратегий полусоединений.
-
strategy: Стратегия полусоединения для включения или выключения. Допустимы следующие имена стратегий:DUPSWEEDOUT,FIRSTMATCH,LOOSESCAN,MATERIALIZATION.Для подсказок
SEMIJOIN, если стратегии не указаны, полусоединение используется, если это возможно, на основе стратегий, включенных в соответствии с переменной системыoptimizer_switch. Если стратегии указаны, но неприменимы для оператора, используетсяDUPSWEEDOUT.Для подсказок
NO_SEMIJOIN, если стратегии не указаны, полусоединение не используется. Если указаны стратегии, исключающие все применимые стратегии для оператора, используетсяDUPSWEEDOUT.
Если один подзапрос вложен в другой, и оба объединены в полусоединение внешнего запроса, любые указания стратегий полусоединений для внутреннего подзапроса игнорируются. Подсказки SEMIJOIN и NO_SEMIJOIN по-прежнему могут использоваться для включения или выключения преобразований полусоединений для таких вложенных подзапросов.
Если DUPSWEEDOUT отключено, иногда оптимизатор может сгенерировать план запроса, который далек от оптимального. Это происходит из-за эвристического обрезки во время жадного поиска, чего можно избежать, установив optimizer_prune_level=0.
Примеры:
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);
Синтаксис подсказок, влияющих на то, использовать ли материализацию подзапроса или преобразования IN-to-EXISTS:
SUBQUERY([@query_block_name] strategy)
Имя подсказки всегда SUBQUERY.
Для подсказок SUBQUERY допустимы следующие значения strategy: INTOEXISTS, MATERIALIZATION.
Примеры:
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);
Для подсказок полусоединения и SUBQUERY ведущее @ указывает блок запроса, к которому относится подсказка. Если подсказка не содержит ведущего query_block_name@, подсказка относится к блоку запроса, в котором она находится. Для присвоения имени блоку запроса см. Подсказки оптимизатора для именования блоков запросов. query_block_name
Если комментарий подсказки содержит несколько подсказок для подзапросов, используется первая. Если есть другие следующие подсказки такого типа, они вызывают предупреждение. Следующие подсказки других типов игнорируются.
Указания оптимизатора для времени выполнения оператора
Указание MAX_EXECUTION_TIME разрешено только для операторов SELECT. Оно устанавливает ограничение N (значение таймаута в миллисекундах) на время выполнения оператора, прежде чем сервер его прервет:
MAX_EXECUTION_TIME(N)
Пример с таймаутом в 1 секунду (1000 миллисекунд):
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 WHERE ...
Указание MAX_EXECUTION_TIME( устанавливает таймаут выполнения оператора в N)N миллисекунд. Если этот параметр отсутствует или N равно 0, применяется таймаут оператора, установленный системной переменной max_execution_time.
Указание MAX_EXECUTION_TIME применяется следующим образом:
Для операторов с несколькими ключевыми словами
SELECT, такими как объединения или операторы с подзапросами, указаниеMAX_EXECUTION_TIMEприменяется ко всему оператору и должно появляться после первого оператораSELECT.Оно применяется к операторам чтения
SELECTтолько для чтения. Операторы, которые не являются только для чтения, — это те, которые вызывают хранимую функцию, изменяющую данные как побочный эффект.Оно не применяется к операторам
SELECTв хранимых программах и игнорируется.
Синтаксис указания установки переменной
Указание SET_VAR временно устанавливает значение системной переменной в сеансе (на время выполнения одного оператора). Примеры:
SELECT /*+ SET_VAR(sort_buffer_size = 16M) */ name FROM people ORDER BY name;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off') */ 1;
Синтаксис указания SET_VAR:
SET_VAR(var_name = value)
var_name указывает имя системной переменной, имеющей значение в сеансе (хотя не все такие переменные можно назвать, как объяснено позже). value — это значение, которое нужно присвоить переменной; значение должно быть скалярным.
SET_VAR производит временное изменение переменной, как показано в этих операторах:
mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 1 |
+-----------------+
mysql> SELECT /*+ SET_VAR(unique_checks=OFF) */ @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 0 |
+-----------------+
mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 1 |
+-----------------+
С SET_VAR нет необходимости сохранять и восстанавливать значение переменной. Это позволяет заменить несколько операторов одним оператором. Рассмотрим эту последовательность операторов:
SET @saved_val = @@SESSION.var_name;
SET @@SESSION.var_name = value;
SELECT ...
SET @@SESSION.var_name = @saved_val;
Последовательность можно заменить этим одиночным оператором:
SELECT /*+ SET_VAR(var_name = value) ...
Операторы SET позволяют использовать любой из этих синтаксисов для именования переменных сеанса:
SET SESSION var_name = value;
SET @@SESSION.var_name = value;
SET @@.var_name = value;
Поскольку указание SET_VAR применяется только к переменным сеанса, область действия сеанса неявна, и SESSION, @@SESSION. и @@ не нужны и не разрешены. Включение явного синтаксиса индикатора сеанса приведет к игнорированию указания SET_VAR с предупреждением.
Не все переменные сеанса разрешены для использования с SET_VAR. В описаниях отдельных системных переменных указано, разрешена ли каждая переменная для указаний; см. Раздел 7.1.8, «Системные переменные сервера». Вы также можете проверить системную переменную во время выполнения, попытавшись использовать ее с SET_VAR. Если переменная не разрешена для указаний, появляется предупреждение:
mysql> SELECT /*+ SET_VAR(collation_server = 'utf8mb4') */ 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Warning
Code: 4537
Message: Variable 'collation_server' cannot be set using SET_VAR hint.
Синтаксис SET_VAR позволяет установить только одну переменную, но можно использовать несколько указаний для установки нескольких переменных:
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off')
SET_VAR(max_heap_table_size = 1G) */ 1;
Если несколько указаний с одним и тем же именем переменной появляются в одном операторе, применяется первое, а остальные игнорируются с предупреждением:
SELECT /*+ SET_VAR(max_heap_table_size = 1G)
SET_VAR(max_heap_table_size = 3G) */ 1;
В этом случае второе указание игнорируется с предупреждением о конфликте.
Указание SET_VAR игнорируется с предупреждением, если нет системной переменной с указанным именем или значение переменной некорректно:
SELECT /*+ SET_VAR(max_size = 1G) */ 1;
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=yes') */ 1;
Для первого оператора нет переменной max_size. Для второго оператора mrr_cost_based принимает значения on или off, поэтому попытка установить ее в yes некорректна. В каждом случае указание игнорируется с предупреждением.
Указание SET_VAR разрешено только на уровне оператора. Если используется в подзапросе, указание игнорируется с предупреждением.
Реплики игнорируют указания SET_VAR в реплицированных операторах, чтобы избежать потенциальных проблем безопасности.
Синтаксис указания группы ресурсов
Указание оптимизатора RESOURCE_GROUP используется для управления группами ресурсов (см. Раздел 7.1.16, «Группы ресурсов»). Это указание временно (на время выполнения оператора) назначает поток, выполняющий оператор, указанной группе ресурсов. Требуется привилегия RESOURCE_GROUP_ADMIN или RESOURCE_GROUP_USER.
Примеры:
SELECT /*+ RESOURCE_GROUP(USR_default) */ name FROM people ORDER BY name;
INSERT /*+ RESOURCE_GROUP(Batch) */ INTO t2 VALUES(2);
Синтаксис указания RESOURCE_GROUP:
RESOURCE_GROUP(group_name)
group_name указывает группу ресурсов, которой должен быть назначен поток на время выполнения оператора. Если группы не существует, появляется предупреждение, и указание игнорируется.
Указание RESOURCE_GROUP должно появляться после начального ключевого слова оператора (SELECT, INSERT, REPLACE, UPDATE или DELETE).
Альтернативой RESOURCE_GROUP является оператор SET RESOURCE GROUP, который постоянно назначает потоки группе ресурсов. См. Раздел 15.7.2.4, «Оператор SET RESOURCE GROUP».
Указания оптимизатору для именования блоков запросов
Указания оптимизатора на уровне таблиц, индексов и подзапросов позволяют именовать отдельные блоки запросов в качестве аргументов. Для создания этих имён используйте указание QB_NAME, которое присваивает имя блоку запроса, в котором оно используется:
QB_NAME(name)
Указания QB_NAME позволяют чётко указать, к каким блокам запросов относятся другие указания. Они также позволяют указать все указания, не связанные с именами блоков запросов, в одном комментарии к указаниям, что упрощает понимание сложных операторов. Рассмотрим следующий оператор:
SELECT ...
FROM (SELECT ...
FROM (SELECT ... FROM ...)) ...
Указания QB_NAME присваивают имена блокам запросов в операторе:
SELECT /*+ QB_NAME(qb1) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...
Затем другие указания могут использовать эти имена для ссылки на соответствующие блоки запросов:
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 ...)) ...
Результат следующий:
MRR(@qb1 t1)относится к таблицеt1в блоке запросаqb1.BKA(@qb2)относится к блоку запросаqb2.NO_MRR(@qb3 t1 idx1, id2)относится к индексамidx1иidx2в таблицеt1в блоке запросаqb3.
Имена блоков запросов являются идентификаторами и следуют обычным правилам относительно допустимых имён и способа их цитирования (см. Раздел 11.2, «Имена объектов схемы»). Например, имя блока запроса, содержащее пробелы, должно быть заключено в кавычки, что можно сделать с помощью обратных кавычек:
SELECT /*+ BKA(@`my hint name`) */ ...
FROM (SELECT /*+ QB_NAME(`my hint name`) */ ...) ...
Если включён режим SQL ANSI_QUOTES, можно также заключить имя блока запроса в двойные кавычки:
SELECT /*+ BKA(@"my hint name") */ ...
FROM (SELECT /*+ QB_NAME("my hint name") */ ...) ...
© 2025 Oracle
Licensed under the GPLv2 License.