MySQL: избегайте сортировки файлов при использовании IN и ORDER BY - PullRequest
1 голос
/ 17 сентября 2009

Предположим, у меня есть следующая таблица (назовем ее my_table):

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   KEY `my_field` (`my_field`,`table_id`)
 ) ENGINE=MyISAM

Первичный ключ для my_table - table_id (auto_increment), и у меня также есть ключ с my_field и table_id.

Если я проверю этот запрос ...

EXPLAIN SELECT * FROM my_table
WHERE my_field = 28
ORDER BY table_id DESC;

... Я получаю:

id  select_type  table     type  possible_keys  key       key_len  ref    rows  Extra
--- -----------  --------  ----  -------------  --------  -------  -----  ----  -----
1   SIMPLE       my_table  ref   my_field       my_field  8        const  36

Вы видите, что он использует правильный ключ (my_field).

Но если я попробую это ...

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;

... я получаю:

id  select_type  table     type  possible_keys  key     key_len  ref     rows  Extra
--- -----------  --------  ----  -------------  ------  -------  ------  ----  ---------------------------
1   SIMPLE       my_table  ALL   my_field       (NULL)  (NULL)   (NULL)  406   Using where; Using filesort

Вы видите, что он вообще не использует какую-либо клавишу, и, что еще хуже, использует filesort .

Даже если я делаю "FORCE INDEX (my_field)", он все равно выполняет сортировку файлов.

Есть ли способ избежать сортировки файлов?

Ответы [ 4 ]

13 голосов
/ 18 сентября 2009

Насколько я понимаю, MySQL не может использовать индекс для сортировки этого запроса.

MySQL может использовать индекс только в том случае, если он отсортирован так же, как ваш запрос. Допустим, ваши записи для (table_id,my_field) равны

(1,1), (2,28), (3,14), (4,20)

Индекс на (my_field,table_id) будет храниться так

(1,1), (14,3), (20,4), (28,2)

При выполнении запроса из вашего примера IN (для простоты мы скажем, что ваш ORDER BY - ASCending) , MySQL найдет

(1,1), (20,4), (28,2)

... в этом порядке. Независимо от того, что они должны будут отсортировать их в (1,1),(28,2),(20,4). Это сортировка файлов. Вот почему MySQL мог использовать этот индекс только в том случае, если запрос был ORDER BY my_field или ORDER BY my_field, table_id, потому что индекс уже в этом порядке. Именно поэтому он не может [в настоящее время в некоторых будущих версиях может быть разрешена сортировка составных индексов в смешанном порядке] используйте индекс, если вы смешиваете ASC и DESC. Индекс сортируется в ASC, ASC и независимо от того, каким образом вы его читаете, он не будет в правильном порядке.

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

Если вам нужно отсортировать тысячи строк, вы можете получить лучшие результаты, используя небольшую производную таблицу, особенно если каждая строка действительно большая (много полей, больших двоичных объектов и т. Д.)

  SELECT t.*
    FROM (
          SELECT table_id FROM my_table WHERE my_field IN (1, 28, 20)
         ) tmp
    JOIN my_table t USING (table_id)
ORDER BY t.table_id DESC

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

1 голос
/ 17 сентября 2009

Ваш запрос имеет ошибочный знак "=". Удалите это так:

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;
1 голос
/ 17 сентября 2009

Он не будет использовать ключ my_field, потому что это ключ, основанный как на table_id, так и my_field. Если вы выполняете запрос, основанный только на my_field, у вас должен быть индекс исключительно на my_field.

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

1 голос
/ 17 сентября 2009

Во-первых, я думаю, что ваши ключи задом наперед в вашем SQL. Разве вы не хотите, чтобы первичный ключ был table_id?

Это, вероятно, то, что вы хотите:

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   INDEX `my_index` (`my_field`)
 ) ENGINE=MyISAM
...