Подтвердить что ты не робот

MySQL не использует индексы ( "Использование filesort" ) при использовании ORDER BY

Я сталкиваюсь с некоторыми довольно важными проблемами, связанными с использованием "ORDER BY" -statements в моем SQL-коде.

Все в порядке, пока я не использую ORDER BY-statements в SQL. Однако, как только я вводю ORDER BY: s в коде SQL, все резко замедляется из-за отсутствия правильной индексации. Можно было бы предположить, что исправление этого было бы тривиальным, но, судя по дискуссиям на форуме, и т.д., Это, кажется, довольно распространенная проблема, которую я еще не видел окончательного и краткого ответа на этот вопрос.

Вопрос: Учитывая следующую таблицу...

CREATE TABLE values_table (
  id int(11) NOT NULL auto_increment,
  ...
  value1 int(10) unsigned NOT NULL default '0',
  value2 int(11) NOT NULL default '0',
  PRIMARY KEY  (id),
  KEY value1 (value1),
  KEY value2 (value2),
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;

... как создать индексы, которые будут использоваться при запросе таблицы для value1 -range при сортировке по значению value2?

В настоящее время выборка выполняется, когда НЕ используется предложение ORDER BY.

См. следующий вывод EXPLAIN QUERY:

OK, when NOT using ORDER BY:

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10;

+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | this_ | range | value1        | value1   | 4       | NULL | 3303 | Using where |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
However, when using ORDER BY I get "Using filesort":

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10;

+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                       |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | this_ | range | value1        | value1   | 4       | NULL | 3303 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+

Дополнительная информация о содержимом таблицы:

SELECT MIN(value1), MAX(value1) FROM values_table;
+---------------+---------------+
| MIN(value1)   | MAX(value2)   |
+---------------+---------------+
|             0 |    4294967295 |
+---------------+---------------+

...

SELECT MIN(value2), MAX(value2) FROM values_table;
+---------------+---------------+
| MIN(value2)   | MAX(value2)   |
+---------------+---------------+
|             1 |        953359 |
+---------------+---------------+

Пожалуйста, дайте мне знать, нужна ли какая-либо дополнительная информация для ответа на вопрос.

Большое спасибо!

Обновление # 1: Добавление нового составного индекса (ALTER TABLE values_table ADD INDEX (значение1, значение2);) не решает проблему. После добавления такого индекса вы все равно получите "Using filesort".

Обновление # 2: Ограничение, о котором я не упоминал в моем вопросе, состоит в том, что я бы скорее изменил структуру таблицы (например, добавив индексы и т.д.), чем изменив используемые SQL-запросы, Запросы SQL автоматически генерируются с использованием Hibernate, поэтому рассмотрите те более или менее фиксированные.

4b9b3361

Ответ 1

Вы не можете использовать индекс в этом случае, так как вы используете условие фильтрации RANGE.

Если вы используете что-то вроде:

SELECT  *
FROM    values_table this_
WHERE   this_.value1 = @value
ORDER BY
        value2
LIMIT 10

то создание составного индекса на (VALUE1, VALUE2) будет использоваться как для фильтрации, так и для упорядочения.

Но вы используете условие диапазона, поэтому вам все равно придется выполнять заказы.

Ваш составной индекс будет выглядеть следующим образом:

value1 value2
-----  ------
1      10
1      20
1      30
1      40
1      50
1      60
2      10
2      20
2      30
3      10
3      20
3      30
3      40

и если вы выберете 1 и 2 в value1, вы все равно не получите весь отсортированный набор value2.

Если ваш индекс на value2 не очень избирателен (например, в таблице не так много DISTINCT value2), вы можете попробовать:

CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1)

/* Note the order, it important */    

SELECT  *
FROM    (
        SELECT  DISTINCT value2
        FROM    mytable
        ORDER BY
                value2
        ) q,
        mytable m
WHERE   m.value2 >= q.value2
        AND m.value2 <= q.value2
        AND m.value1 BETWEEN 13123123 AND 123123123

Это называется методом доступа SKIP SCAN. MySQL не поддерживает его напрямую, но его можно эмулировать следующим образом.

В этом случае будет использоваться доступ RANGE, но, вероятно, вы не получите каких-либо преимуществ по производительности, если DISTINCT value2 содержит меньше, чем около 1% строк.

Примечание:

m.value2 >= q.value2
AND m.value2 <= q.value2

вместо

m.value2 = q.value2

Это делает MySQL выполнение RANGE проверки каждого цикла.

Ответ 2

Мне кажется, что у вас есть два полностью независимых ключа: один для value1 и один для value2.

Поэтому, когда вы используете ключ value1 для извлечения, записи не обязательно возвращаются в порядке значения2, поэтому их нужно сортировать. Это все еще лучше, чем полное сканирование таблицы, поскольку вы сортируете записи, удовлетворяющие вашему предложению "where value1".

Я думаю (если это возможно в MySQL), сложный ключ (value1, value2) разрешил бы это.

Try:

CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment,
    ...
    value1 int(10) unsigned NOT NULL default '0',
    value2 int(11) NOT NULL default '0',
    PRIMARY KEY  (id),
    KEY value1 (value1),
    KEY value1and2 (value1,value2),
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;

(или эквивалент ALTER TABLE), предполагая, что правильный синтаксис в MySQL для составного ключа.

Во всех базах данных, которые я знаю (и я должен признать, что MySQL не является одним из них), это заставит механизм БД выбрать ключ value1and2 для извлечения строк, и они уже будут отсортированы в value2-in-value1 порядок, поэтому не потребуется сортировка файла.

Вы все равно можете сохранить ключ value2, если вам это нужно.