MySQL не использует индексы («Использование файловой сортировки») при использовании ORDER BY - PullRequest
18 голосов
/ 07 апреля 2009

Я столкнулся с некоторыми довольно серьезными проблемами производительности из-за использования в моем SQL-коде «ORDER BY».

Все хорошо, если я не использую операторы ORDER BY в 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 при сортировке по значению value2 ?

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

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

<b>OK, when NOT using ORDER BY:</b>

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 |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
<b>However, when using ORDER BY I get "Using filesort":</b>

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); ) не решает проблему. После добавления такого индекса вы все равно получите «Использование файловой сортировки».

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

Ответы [ 2 ]

19 голосов
/ 07 апреля 2009

В этом случае нельзя использовать индекс, так как вы используете условие фильтрации 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's 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 проверку каждого цикла.

0 голосов
/ 07 апреля 2009

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

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

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

Попробуйте:

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-Within-Value1. порядок, поэтому не нужно сортировать файлы.

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

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