Индекс покрытия используется несмотря на отсутствие столбца

8

У меня есть следующий запрос, используя MariaDB 10 / InnoDB:

SELECT id, sender_id, receiver_id, thread_id, date_created, content 
FROM user_message 
WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Этот запрос выбирает сообщения в соответствии с заданными условиями и сортирует по дате создания.

У меня есть индекс покрытия по (thread_id, date_created).

При запуске EXPLAIN используется правильный индекс, и я получаю вывод «Using where», хотя в запросе используется столбец в середине оператора, которого нет в индексе. Я могу использовать любое значение для "placeholder = x", и результат будет таким же.

Если я изменяю сортировку, чтобы использовать другой столбец, EXPLAIN правильно указывает «Использование где. Использование файловой сортировки».

У меня головокружительный момент. Может ли кто-нибудь пролить свет на это? Я ожидаю увидеть, что потребуется дополнительная сортировка файлов, поскольку индекс покрытия не может быть использован полностью из-за дополнительного столбца.

Том
источник

Ответы:

8

Случай A
Запрос:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20

Индекс:

(thread_id, date_created)

План:

Index is used
Using Where
Using filesort

Нет проблем там, верно? Если используется индекс (для частичного соответствия WHEREусловию), нам все еще нужна операция сортировки, чтобы упорядочить результаты по some_column(которого нет в индексе). Нам также нужна дополнительная проверка (Using Where), чтобы сохранить только те строки, которые соответствуют 2-му условию. ХОРОШО.


Случай B (вопрос)
Запрос:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Индекс:

(thread_id, date_created)

План:

Index is used
Using Where
-- no "Using filesort"

Так почему же здесь не нужна сортировка ? Потому что индекса достаточно для сортировки по запросу. Конечно, существует дополнительная проблема дополнительного условия ( AND placeholder = FALSE), которое не включено в индекс.

Хорошо, но нам здесь не нужна сортировка. Индекс может предоставить нам результаты, которые соответствуют первому условию ( WHERE thread_id = 12345) и находятся в требуемом порядке для вывода. Единственная дополнительная проверка, которая нам нужна - и то, что делает план - это получить строки из таблицы в порядке, указанном в индексе, и проверять это 2-е условие, пока мы не получим 20 совпадений. Вот что означает ** Используя Где "".

Мы можем получить 20 совпадений в первых 20 строках (что действительно хорошо и быстро) или в первых 100 (все еще, вероятно, достаточно быстро) или в первых 1000000 (вероятно, очень, очень медленно), или мы можем получить только 19 совпадений из таблица даже после чтения всех соответствующих строк из индекса (действительно очень медленно на большой таблице). Все зависит от распределения данных.


Случай C (даже лучший план)
Запрос:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Индекс:

(placeholder, thread_id, date_created)

План:

Index is used
-- no "Using Where"
-- no "Using filesort"

Теперь наш индекс соответствует как условиям, так и порядку. План довольно прост: получить первый * 20 матчей из индекса и прочитать соответствующие строки из таблицы. Никакой дополнительной проверки (без «Использования где») и никакой сортировки (без «Использование сортировки файлов») не требуется.

first *: первые 20 при чтении индекса назад от конца (как у нас ORDER BY .. DESC), но это не проблема. Индексы B-дерева можно читать вперед и назад с почти одинаковой производительностью.

ypercubeᵀᴹ
источник
7
  • Использование индекса указывает на « Сопроводительное индекс» - все столбцы в любом месте в SELECTнаходятся где - нибудь в одном индексе. Итак, у вас нет «покрывающего» индекса. И нецелесообразно создавать закрывающий индекс для вашего запроса (упомянуто слишком много столбцов).
  • Использование где - в основном шум.
  • Использование сортировки файлов - запрос требует сортировки, но он может быть в ОЗУ или во временной таблице. И может быть несколько сортов (например, GROUP BY x ORDER BY b)
  • Любой из них позволит взглянуть только на 20 рядов; любой другой индекс потребует больше строк, возможно, всей таблицы:

    INDEX(thread_id, placeholder, date_created)
    INDEX(placeholder, thread_id, date_created)
  • Нет, количество элементов составного индекса не имеет значения при упорядочении столбцов в индексе.

Моя кулинарная книга объясняет, как получить оптимальный индекс, учитывая a SELECT.

Рик Джеймс
источник
Спасибо за поваренную книгу - очень хороший лист.
Том