MySQL: почему Select .. IN с подзапросом не может использовать индекс - PullRequest
0 голосов
/ 06 ноября 2018

Я начал изучать MySQL и столкнулся с некоторыми проблемами, касающимися индексации подзапроса или соединения. У меня есть две таблицы, созданные следующим образом

create table User(id integer, poster integer, PRIMARY KEY (id,poster));
insert into User(id, poster) values(1, 123);
insert into User(id, poster) values(1, 345);
insert into User(id, poster) values(2, 123);


create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),  INDEX(poster),INDEX(time,c));
insert into Feed(id, poster, c,time) values(1, 123, 0, 2);
insert into Feed(id, poster, c,time) values(2, 123,1,1);
insert into Feed(id, poster, c,time) values(3, 345,2,3);

Сначала я попробовал несколько простых запросов, таких как

1. Select poster from User where id =1;  
2. Select c from Feed where poster = 1;
3. Select c from Feed where poster in (1,2,3) 

Третий запрос объяснения выглядит как

SIMPLE  Feed    NULL    ALL poster  NULL    NULL    NULL    3   100.00  Using where; Using filesort

Я не уверен, зачем нужна сортировка файлов. Однако после добавления составного индекса INDEX (time, poster, c) в таблицу Feed. В том же запросе будет использоваться индекс

Вот новый запрос на создание таблицы

   create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),INDEX(time,poster, c));

Вот объяснение вывода с новым составным индексом 1 SIMPLE Feed NULL index NULL time 15 NULL 3 50.00 Использование где; Используя индекс

Полагаю, так как order by имеет более высокий приоритет и это самый левый индекс, поэтому мы использовали его в первую очередь. Затем, добавив плакат в составной индекс, мы сможем по-прежнему использовать этот составной индекс для фильтрации и, наконец, вернуть c.

Тогда я попробовал какой-то подзапрос

explain SELECT Feed.c from Feed where Feed.poster IN(select poster from User where id =1) order by Feed.time; 

Ничего особенного, я просто заменяю жестко (1,2,3) на подзапрос. Я ожидаю увидеть тот же результат объяснения, но вместо этого я получаю

1   SIMPLE  User    NULL    ref PRIMARY,poster  PRIMARY 4   const   1   100.00  Using index; Using temporary; Using filesort
1   SIMPLE  Feed    NULL    index   NULL    time    15  NULL    3   33.33   Using where; Using index; Using join buffer (Block Nested Loop)

Мне любопытно, почему в таблице USER используется команда «Временный»; Использование сортировки файлов. Я также попытался покинуть присоединение, он также имеет такой же вывод вывода

explain SELECT Feed.c
FROM `Feed` 
LEFT JOIN `User` on User.poster = Feed.poster where User.id = 1 order by Feed.time;

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

Как мне оптимизировать индексацию и запросы?

Спасибо

1 Ответ

0 голосов
/ 06 ноября 2018

Дело не в том, что не может , а в том, что нет никакой выгоды.

Индекс немного похож на другую таблицу, к которой можно сначала присоединиться, чтобы помочь с присоединением к реальной таблице.

В вашем случае быстрее отсканировать таблицу. Альтернативой может быть использование индекса, чтобы изолировать, какие строки в базовой таблице требуются и , а затем перейти к базовой таблице, чтобы получить эти строки.

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

Итак, напишите тестовый стенд, который создает МНОГО больше случайных данных, тогда вы сможете его увидеть.


В качестве альтернативы используйте индекс покрытия. Тот, который содержит все столбцы, необходимые для поиска, И все столбцы, которые вы включите в SELECT и JOIN.

В приведенном ниже примере я меняю (для таблицы Feed) INDEX(poster) на INDEX(poster, c). Теперь, если планировщик запросов читает из индекса, он также сразу узнает значение c, не «присоединяясь» к базовой таблице.

create table User(id integer, poster integer, PRIMARY KEY (id,poster), INDEX(poster));
insert into User(id, poster) values(1, 123);
insert into User(id, poster) values(1, 345);
insert into User(id, poster) values(2, 123);

create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),  INDEX(poster, c),INDEX(time,c));
insert into Feed(id, poster, c,time) values(1, 123, 0, 2);
insert into Feed(id, poster, c,time) values(2, 123,1,1);
insert into Feed(id, poster, c,time) values(3, 345,2,3);

Теперь сравните два запроса ...

Select c from Feed where poster in (1,2,3)

SELECT c, time FROM feed WHERE poster IN (1,2,3)

На первый может ответить только указатель.

Второму необходимо либо просканировать всю таблицу, либо выполнить поиск по индексу И присоединиться к таблице. Поскольку таблица очень мала, оптимизатор решит просто отсканировать всю таблицу, так как это будет дешевле.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...