Есть как минимум 3 причины, по которым ваш выбор не использует индекс
1) Вы использовали select *
, который включает элементы, не входящие в индекс (т. е. invitation_id
). Это означает, что если бы он использовал индекс, ему пришлось бы искать строку в базе данных, чтобы получить значение invitation_id
. Если бы вы добавили invitation_id
к индексу, он бы использовал этот индекс. Если бы вы сделали select
всего лишь invitee_id, inviter_id
, он бы использовал индекс.
2) Оптимизатор запросов решил, что будет лучше просто просмотреть таблицу, а не диапазон индекса. Когда оптимизатор пытается выбрать полное сканирование таблицы или частичное сканирование индекса, он не делает этого для конкретного запроса — ему нужен план, который в целом работает хорошо. Тот, который может быть запущен снова и снова. Сканирование от invitee_id,inviter_id
(62,70)
до (70,62)
, скорее всего, даст только 8 элементов индекса, но при случайном выборе из 50 000 элементов среднее расстояние составит около 17 000 элементов. Таким образом, в среднем один запрос будет обращаться к 1/3 индекса (т. е. извлекать его в память), а затем обращаться к странице, на которой находится строка (см. № 1), извлекая ее в память. Ваши строки настолько малы, что доступ только к одному элементу, вероятно, приведет к 680 строкам (страница 8 КБ по 12 байтам для 3 32-битных #), что составляет 1/70 таблицы - выполните 100 запросов и, вероятно, вы вытащили весь индекс в памяти и всей таблицы — имеет смысл потратить немного больше времени на сканирование таблицы и использовать на 40% меньше памяти для хранения битов других таблиц. В какой-то момент (который кажется 65 тыс. строк) это перестает иметь смысл.
3) Что сказал ваш вопрос: вы использовали ИЛИ. Выражение ИЛИ нельзя использовать для поиска чего-либо в индексе, то есть вы не можете искать 62 или 70. Вместо этого оно создает диапазон, ищущий (62,70)
, затем сканирует, чтобы добраться до (70,62)
(см. # 2, почему это может быть плохо).
Вы спросили "что здесь не так" - это то, что вы использовали ИЛИ, которое не масштабируется. Вам нужно избегать не только типа ALL, но и большого типа RANGES.
Я видел ту же проблему с другими механизмами SQL, и решение, которое я использовал, было UNION ALL.
Что-то типа
SELECT * FROM `invitation` WHERE
(invitee_id = 70 AND inviter_id = 62)
UNION ALL
SELECT * FROM `invitation` WHERE
(invitee_id = 62 AND inviter_id = 70)
Это сделает это двумя запросами и объединит результаты без проверки дубликатов.
Это намного проще в использовании памяти и намного быстрее - требуется всего несколько страниц индекса и две страницы из таблицы и O (log (N)) для каждого поиска. Это потому, что теперь он имеет тип const - ваша цель состояла в том, чтобы исключить ALL, но переключение на RANGE почти так же плохо, как выборка всего двух строк. Сканирование всей таблицы равно O(N), а сканирование RANGE индекса также равно O(N), поскольку O(1/3*N) равно O(N). Другими словами, он не масштабируется.
person
Tony Lee
schedule
15.12.2012
EXPLAIN
. - person Salman A   schedule 15.12.2012