Как MySQL использует многопольные индексы для запросов с ИЛИ в среднем поле индекса? - PullRequest
0 голосов
/ 05 июля 2018

У меня есть элементы таблицы InnoDB с многоколоночным неуникальным индексом (group_id, type_id, expiry_date).

В случае запроса SELECT * FROM Items WHERE group_id = 1 AND type_id IN (1,2,3) AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'

Будет ли индекс работать нормально, так как я использую IN для второго поля индекса, и у них дополнительно есть диапазон для 3-й воли, или я должен извлечь выгоду из его разбивки на?

SELECT * FROM Items WHERE group_id = 1 AND type_id = 1 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01' UNION SELECT * FROM Items WHERE group_id = 1 AND type_id = 2 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01' UNION SELECT * FROM Items WHERE group_id = 1 AND type_id = 3 AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'

EXPLAIN показывает одинаковые планы запросов для обоих запросов, но у меня есть небольшая таблица для тестирования, и я не уверен, будет ли оптимизатор запросов действовать одинаково на больших объемах данных.

И как вообще работает индекс для случая, использующего IN/OR/BETWEEN для 2 последовательных полей в индексе?

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Какая версия MySQL / MariaDB? Недавно были оптимизации. не сейчас, если они помогут здесь.

У вас есть возможная ошибка - в том числе дополнительный день в AND expiry_date BETWEEN '2017-01-01' AND '2018-01-01'. Изменить на

AND expiry_date >= '2017-01-01'
AND expiry_date  < '2017-01-01' + INTERVAL 1 YEAR

(Это считается одним тестом «диапазона». BETWEEN также является тестом диапазона, но это «включительно», следовательно, «ошибка».)

У меня просто было бы два составных индекса (если бы я не мог найти реальный ответ на ваш вопрос):

(group_id, type_id, expiry_date)
(group_id, expiry_date)

Случай 1: Оптимизатор может пройти мимо IN: тогда работает первый индекс.

Случай 2: оптимизатор не может пройти мимо IN: Тогда происходит одно из следующих действий:

  • В списке IN есть только один элемент. Затем он преобразуется из IN в =, и первый индекс является оптимальным, при этом используются все 3 столбца.
  • Оптимизатор решает, что первый индекс лучше - маленький список IN, большой диапазон дат.
  • Оптимизатор решает, что диапазон дат лучше (меньший диапазон), и выбирает второй индекс.

Подход UNION может или не может быть лучше в этой ситуации. Сбор данных во временную таблицу требует много времени. Временная таблица была недавно удалена, но только для некоторых случаев UNION ALL.

Да, используйте UNION ALL. Это исключает сортировку и, возможно, дополнительную временную таблицу.

Тест с большим набором данных. Для строк размером менее 1 КБ производительность вряд ли будет иметь значение.

Правило большого пальца в упорядочении столбцов в индексе:

  1. = тест (ы)
  2. IN, если есть
  3. один «диапазон» (BETWEEN, < и т. Д.), Если есть
  4. Подумайте о создании индекса покрытия.

Мой Кулинарная книга

Существуют другие оптимизации, которые зависят от того, что находится в * в SELECT *.

0 голосов
/ 05 июля 2018

Для второго запроса используйте union all вместо union. Вы всегда хотите union all, если только вы не хотите нести дубликаты.

Я полагаю, что вам пригодится второй запрос для больших данных. Я не думаю, что MySQL поддерживает пропуски по индексам, поэтому индекс используется только для group_id и type_id, но не напрямую для даты.

...