Узел вторичного индекса InnoDB leaf включает значения первичного ключа, но если вы хотите выполнить запрос диапазона по значению идентификатора, ему нужны узлы non-leaf индекс для включения значений первичного ключа.
Если вы выбираете только идентификатор в вашем списке выбора, тогда избыточно добавлять первичный ключ в определение индекса. Например:
CREATE TABLE Favorite (
id INT AUTO_INCREMENT PRIMARY KEY,
something INT,
KEY s (something),
KEY s_with_id (something, ID)
);
Любой индекс сделает следующий запрос запросом только для индекса. InnoDB предпочитает более компактный индекс s
. Это может быть запрос только для индекса, поскольку конечные узлы индекса предоставляют значение идентификатора.
mysql> EXPLAIN SELECT something, ID FROM Favorite WHERE something = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: Favorite
type: ref
possible_keys: s,s_with_id
key: s
key_len: 5
ref: const
rows: 48
Extra: Using where; Using index
Но в случае, когда у вас также есть условие неравенства или диапазона для идентификатора, оно получит большую выгоду от индекса, который также включает значения идентификатора в неконечных узлах. Это может использовать тот факт, что значения идентификаторов сортируются в B-дереве.
mysql> EXPLAIN SELECT something, ID FROM Favorite WHERE something = 1 and id < 10 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: Favorite
type: ref
possible_keys: PRIMARY,s,s_with_id
key: s_with_id
key_len: 9
ref: const
rows: 1
Extra: Using where; Using index
PS: Пожалуйста, не используйте термин «кластеризованный» при описании составного индекса, потому что кластеризованный означает что-то другое в отношении индексов. Кластерный индекс изменяет хранение табличных данных в соответствии с порядком индекса. Первичные ключи InnoDB всегда являются кластеризованным индексом, так как строка данных хранится в конечном узле индекса первичного ключа.
Ваш комментарий: имейте в виду, что запрос "диапазона" по первичному индексу может быть лучше запроса "ref" по вторичному индексу.
Когда ваш запрос использует вторичный индекс, он в основном должен сделать два обхода дерева в строке: сначала выполнить поиск вторичного индекса, чтобы добраться до конечного узла, где он находит значение первичного ключа, затем второй использовать это значение первичного ключа для поиска первичного (кластеризованного) индекса, чтобы получить остальные столбцы.
В целом для вашего запроса может быть дешевле выполнить запрос диапазона по первичному индексу, поэтому он находит достаточно маленькое подмножество строк и затем применяет другие ваши условия к столбцам, которые он находит. Он не использует вторичный индекс, но все равно выиграл, потому что ему нужно было сделать только один обход дерева на строку.
Я говорю «могло бы быть» не для того, чтобы использовать слова ласки, а потому, что лучший выбор действительно зависит от того, сколько строк соответствует одному из условий. Обычно оптимизатор хорошо справляется с этой оценкой, поэтому нет необходимости использовать FORCE INDEX для переопределения его поведения.