MariaDB 13.0: хинты оптимизатору в запросе и внутри VIEW

9 ноября 2026 года на блоге MariaDB.org вышел разбор хинтов оптимизатора в Server 13.0. Текст написал Олег Смирнов, старший инженер MariaDB plc. Страницу подписал Фредерик Декамп, рубрика Using MariaDB.

Хинт живёт в тексте одного SQL-запроса, обычным комментарием. Им можно подсказать оптимизатору порядок соединения, индекс или отдельный приём, когда про данные известно то, чего оптимизатор не видит. Из-за такого комментария MariaDB не перезапускают. nginx и php-fpm тоже не трогают.

Синтаксис /*+ */ как в MySQL 8.0

MariaDB взяла запись, которую MySQL ввёл в версии 8.0. Тот же вид комментария использует TiDB. Сразу после SELECT стоит /*+ ... */. Внутри одна или несколько подсказок. Так помечают и внешний запрос, и вложенный SELECT.

SELECT /*+ hint1(arguments) hint2(arguments) */ ... FROM t1
WHERE a IN (SELECT /*+ hint3(arguments) */ ...);

Имена hint1 и hint3 в этом фрагменте учебные: так пример и записан у MariaDB. В рабочий запрос ставят конкретный хинт, не эти заглушки.

Подсказкой можно управлять вот чем:

  • в каком порядке соединять таблицы;
  • какой индекс брать, а какой не трогать;
  • пользоваться ли буфером соединения (join buffer);
  • раскрывать ли подзапрос в FROM и вливать его в основной запрос;
  • передавать ли условие внутрь такого подзапроса;
  • как выполнять подзапрос;
  • включать ли отдельные приёмы: MRR, ICP, поиск по диапазону, фильтр по rowid, lateral derived;
  • сколько запросу можно работать. За это отвечает хинт MAX_EXECUTION_TIME.

Имена хинтов у MariaDB и MySQL почти совпадают, мелкие отличия есть. В представление MySQL пускает не всё. Полный список с примерами собран в документации MariaDB. Если хинт ведёт себя не так или нужной подсказки нет, тикет просят открывать в Jira MariaDB.

В VIEW можно не только индекс

В определении представления MySQL разрешает только индексные хинты. Пример из разбора выглядит так:

CREATE VIEW v1 AS SELECT /*+ NO_INDEX(t1) */ a FROM t1;

NO_INDEX(t1) запрещает индексный доступ к t1. Другие подсказки MySQL в тело VIEW не берёт.

В MariaDB такого ограничения нет. В CREATE VIEW можно поставить любые хинты, кроме MAX_EXECUTION_TIME. Второй пример из того же разбора, таблицы учебные:

CREATE VIEW v1 AS SELECT /*+ NO_MRR(t1 idx_a) JOIN_ORDER(t2, t1)*/ t1.a, t2.a
FROM t1, t2
WHERE t1.a = t2.b;

Здесь две подсказки сразу. NO_MRR(t1 idx_a) отключает приём MRR для индекса idx_a на таблице t1. JOIN_ORDER(t2, t1) задаёт порядок соединения: сначала t2, потом t1. Связь в примере обычная, t1.a = t2.b.

Хинт внутри представления может называть только объекты этого представления. Таблицу снаружи вписать нельзя. Лимит времени туда тоже не кладут. MAX_EXECUTION_TIME пишут в том запросе, который запускают сейчас, а не в CREATE VIEW.

На сайте с WordPress ядро такие представления не создаёт. Запросы к таблицам wp_* идут как раньше. Хинт нужен, когда своё представление или тяжёлый отчёт стабильно выбирает плохой план и уже понятно, что именно поправить. Зашили JOIN_ORDER или NO_MRR в VIEW, и подсказка остаётся в определении. Каждый следующий запрос через это представление её подхватит.

Перед заменой определения сохраните текущий текст. Команда SHOW CREATE VIEW и обычный дамп схемы. Пример с t1, t2 и idx_a на боевую базу не переносят: таких имён у WordPress нет.

Представление, перенесённое с MySQL, где внутри был только NO_INDEX, на MariaDB 13.0 читается тем же синтаксисом. Обратный путь уже теснее. JOIN_ORDER и NO_MRR внутри VIEW MySQL в определение представления не берёт, там остаются индексные хинты. Если одна схема живёт и на MariaDB, и на MySQL, подсказки внутри VIEW проверяют на той стороне, куда поедет дамп.

Имя блока запроса без QB_NAME

Вторая своя добавка MariaDB. Чтобы обратиться к внутреннему куску запроса, не обязательно вставлять в него хинт QB_NAME. Сервер понимает явное имя блока, имя по позиции в тексте запроса и имя по алиасу.

Самый гибкий способ: явное имя с путём. Им можно указать объект внутри представления, производной таблицы, общего табличного выражения (CTE) или подзапроса и не дописывать QB_NAME в их текст. Идею пути в MariaDB связывают с TiDB. Там тоже нужно было дотянуться до объекта во внутреннем блоке, не правя сам блок.

Как записывается путь, показано в документации MariaDB, рядом с остальными примерами хинтов.

Хинты есть в Server 13.0

Разбор относится к текущему выпуску MariaDB Server 13.0. Пока версия сервера ниже 13.0, примеры к этой базе не относятся. Сначала смотрят номер, потом копируют комментарий в запрос.

mariadb --version

В клиенте то же самое показывает SELECT VERSION(). В выводе нужна ветка 13.0.

Нет 13.0 в строке версии, значит переписывать запросы сайта рано. Переводить рабочую базу WordPress на новую ветку только ради комментария в SQL незачем. MariaDB прямо пишет, что хинты не замена нормальному оптимизатору. Это рычаг, когда план уже не устраивает.

Путаются обычно в трёх местах. Хинт остался внутри VIEW, а план смотрят по тем же таблицам без представления и не узнают его. В подсказке указана таблица, которой внутри VIEW нет. MAX_EXECUTION_TIME уехал в определение представления, хотя туда его ставить нельзя.

На типичном сайте с WordPress комментарий появляется в своём SQL и только когда сервер уже MariaDB 13.0. Конфиг nginx и wp-config.php из-за этих хинтов не меняют. Если план и так нормальный, комментарий /*+ */ лучше не добавлять.

Источники и ссылки


Комментарии загружаются…