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

#mysql #indexing

#mysql #индексирование

Вопрос:

Мне нужна помощь в этом. У меня есть такой запрос:

 SELECT * FROM cart WHERE
(ts_in between 1249077600 AND 1318975199);
  

В приведенном выше запросе используется индекс btree для ts_in colomn (тип диапазона). Теперь я хочу добавить к этому еще один критерий. Например:

 SELECT * FROM cart WHERE
(ts_in between 1249077600 AND 1318975199) and is_removed=0;
  

Приведенный выше запрос не использует индекс btree для ts_in colomn (диапазон).

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

Я создал два индекса для тестирования :

 CREATE INDEX range_idx_1 using BTREE
ON cart (is_removed, ts_in);
  

и

 CREATE INDEX range_idx_2 using BTREE
ON cart (ts_in , is_removed);
  

Что забавного, когда я использую этот запрос:

 EXPLAIN SELECT id FROM cart WHERE
(ts_in between 1249077600 AND 1318975199) AND is_removed=0;
  

Я получаю эти результаты:

 id | select_type | table | type  | possible_keys           | key         | key_len | ref | rows  | Extra 
1  | SIMPLE      | cart  | range | range_idx_1,range_idx_2 | range_idx_1 | 6       |     | 17391 | Using where; Using index
  

Приведенный выше запрос использует индекс, но:

 EXPLAIN SELECT * FROM cart WHERE
(ts_in between 1249077600 AND 1318975199) AND is_removed=0;
  

У меня такие результаты:

 id | select_type | table | type  | possible_keys           | key         | key_len | ref   | rows  | Extra 
1  | SIMPLE      | cart  | ref   | range_idx_1,range_idx_2 | range_idx_1 | 1       | const | 77979 | Using where
  

При этом индекс не используется.

Когда я пытаюсь ИСПОЛЬЗОВАТЬ синтаксис INDEX или FORCE INDEX, результаты те же. В одном случае Mysql не использует индекс. Любая помощь?

Комментарии:

1. Пожалуйста, опубликуйте план объяснения для каждого запроса.

2. Это таблица MyISAM или InnoDB? Должен ли я предположить, что это InnoDB?

3. Я добавил план выполнения для каждого из приведенных выше сценариев. Кажется, что второй запрос не является диапазоном типов, но почему?

4. @ypercube Это таблица InnoDB

5. Вот почему (потому что это InnoDB), который использует (is_removed, ts_in) индекс, когда вы это делаете SELECT id ... . Первичный ключ хранится внутри каждого индекса в InnoDB, поэтому MySQL не нужно читать таблицу, только для сканирования диапазона индекса, чтобы найти идентификаторы.

Ответ №1:

запустите

 EXPLAIN SELECT * FROM cart WHERE
(ts_in between 1249077600 AND 1318975199) and is_removed=0;
  

чтобы понять, почему оптимизатор запросов mysql выбирает другой индекс.

Mysql не очень хорош в объединении самих индексов, поэтому вам, вероятно, понадобится объединенный индекс для этого запроса. Попробуйте добавить индекс (btree?) для is_removed, ts_in (в этом порядке!).

Вы также можете заставить mysql использовать ваш индекс, добавив ИНДЕКС ИСПОЛЬЗОВАНИЯ к вашему запросу. Иногда это дает лучший результат в качестве индекса, выбранного оптимизатором запросов:

 SELECT * FROM cart USE INDEX '<idx_name>' WHERE
(ts_in between 1249077600 AND 1318975199) and is_removed=0;
  

Комментарии:

1. Я добавляю в вопрос дополнительную информацию. ИСПОЛЬЗОВАТЬ ИНДЕКС или ПРИНУДИТЕЛЬНЫЙ ИНДЕКС не работает. Когда у меня есть в этом запросе «выбрать *», mysql не использует индекс. Например, когда у меня есть «select id», mysql использует правильный индекс, но мне нужно решение для «select *».

2. ‘id’ сохраняется (я предполагаю, что id является первичным ключом) в индексе, поэтому mysql не нужно обращаться к самой таблице для генерации результирующего набора. Но для select * это необходимо, и оптимизатор запросов приходит к выводу, что использование индекса в этом случае не имеет никакого смысла. Для ‘*’ я думаю, вам не повезет, если вы сделаете это простым способом. Возможно, будут работать какие-то каскадные запросы…