Spec-Zone.ru › MySQL 9.2

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 Влияет на обработку объединения Batched Key Access Блок запроса, таблица
BNL, NO_BNL Влияет на оптимизацию объединения hash join Блок запроса, таблица
DERIVED_CONDITION_PUSHDOWN, NO_DERIVED_CONDITION_PUSHDOWN Использовать или игнорировать оптимизацию перемещения производного условия для материализованных производных таблиц Блок запроса, таблица
GROUP_INDEX, NO_GROUP_INDEX Использовать или игнорировать указанный индекс или индексы для сканирования индексов в операциях GROUP BY Индекс
HASH_JOIN, NO_HASH_JOIN Влияет на оптимизацию Hash Join (без эффекта в MySQL 9.2) Блок запроса, таблица
INDEX, NO_INDEX Действует как комбинация JOIN_INDEX, GROUP_INDEX и ORDER_INDEX, или как комбинация NO_JOIN_INDEX, NO_GROUP_INDEX и NO_ORDER_INDEX Индекс
INDEX_MERGE, NO_INDEX_MERGE Влияет на оптимизацию 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 Влияет на оптимизацию Multi-Range Read Таблица, индекс
NO_ICP Влияет на оптимизацию Index Condition Pushdown Таблица, индекс
NO_RANGE_OPTIMIZATION Влияет на оптимизацию диапазонов Таблица, индекс
ORDER_INDEX, NO_ORDER_INDEX Использовать или игнорировать указанный индекс или индексы для сортировки строк Индекс
QB_NAME Присваивает имя блоку запроса Блок запроса
RESOURCE_GROUP Установить группу ресурсов во время выполнения утверждения Глобальный
SEMIJOIN, NO_SEMIJOIN Влияет на стратегии semijoin и antijoin Блок запроса
SKIP_SCAN, NO_SKIP_SCAN Влияет на оптимизацию 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, «Соединения с блочным вложенным циклом и обработкой по ключу»).

  • То, будут ли производные таблицы, ссылки на представления или общие табличные выражения сливаться в внешний блок запроса или материализоваться с помощью внутренней временной таблицы.

  • Использование оптимизации «Проталкивание условия производной таблицы». См. Раздел 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 9.2; используйте 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} в определении представления имеет приоритет над указанием, указанным в запросе, ссылающемся на представление.

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

Указатели оптимизатора на уровне индексов влияют на стратегии обработки индексов, используемые оптимизатором для конкретных таблиц или индексов. Эти типы указателей влияют на использование функции «Проталкивание условия индекса (ICP)», «Многодиапазонное чтение (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: Включают или выключают метод доступа «Объединение индексов» для указанной таблицы или индексов. Дополнительную информацию об этом методе доступа см. в Разделе 10.2.1.3, «Оптимизация объединения индексов». Эти указатели применяются ко всем трём алгоритмам объединения индексов.

      Указатель INDEX_MERGE заставляет оптимизатор использовать объединение индексов для указанной таблицы с использованием указанного набора индексов. Если индекс не указан, оптимизатор рассматривает все возможные комбинации индексов и выбирает наименее затратный вариант. Указатель может быть проигнорирован, если комбинация индексов неприменима к данному оператору.

      Указатель NO_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, «Оптимизация многодиапазонного чтения».

    • NO_ICP: Отключает ICP для указанной таблицы или индексов. По умолчанию ICP — это кандидатская стратегия оптимизации, поэтому указателя для её включения нет. Дополнительную информацию об этом методе доступа см. в Разделе 10.2.1.6, «Оптимизация проталкивания условия индекса».

    • 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 заставляет оптимизатор использовать «Пропуск сканирования» для указанной таблицы с использованием указанного набора индексов. Если индекс не указан, оптимизатор рассматривает все возможные индексы и выбирает наименее затратный вариант. Указатель может быть проигнорирован, если индекс неприменим к данному оператору.

      Указатель NO_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;

В следующих примерах используются указатели объединения индексов, но другие указатели на уровне индексов следуют тем же принципам в отношении игнорирования указателей и приоритета указателей оптимизатора по отношению к системной переменной 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-9.2-en/optimizer-hints.html

Spec-Zone.ru

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