Spec-Zone.ru › MySQL 8.4

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 Доступные подсказки оптимизатору

Таблица 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, подсказка относится к блоку запроса, в котором она находится. Для присвоения имени блоку запроса см. Подсказки оптимизатора для именования блоков запросов.

Если комментарий подсказки содержит несколько подсказок для подзапросов, используется первая. Если есть другие следующие подсказки такого типа, они вызывают предупреждение. Следующие подсказки других типов игнорируются.

END_OF_DOCUMENT_MARKER

Указания оптимизатора для времени выполнения оператора

Указание 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».

END_OF_DOCUMENT_MARKER

Указания оптимизатору для именования блоков запросов

Указания оптимизатора на уровне таблиц, индексов и подзапросов позволяют именовать отдельные блоки запросов в качестве аргументов. Для создания этих имён используйте указание 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.
https://docs.oracle.com/cd/E17952_01/mysql-8.4-en/optimizer-hints.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API