Почему удаление этого индекса в MySQL ускоряет мой запрос в 100 раз? - PullRequest
10 голосов
/ 29 сентября 2011

У меня есть следующая таблица MySQL (упрощенно):

CREATE TABLE `track` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(256) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `is_active` (`is_active`, `id`)
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8

В столбце is_active отмечены строки, которые я хочу игнорировать в большинстве, но не во всех моих запросах. У меня есть несколько запросов, которые периодически читают фрагменты из этой таблицы. Один из них выглядит так:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10;

Выполнение этого запроса занимает более минуты. Вот план выполнения:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10;
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+
| id | select_type | table | type | possible_keys  | key    | key_len | ref   | rows    | Extra       |
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+
|  1 | SIMPLE      | t     | ref  | PRIMARY,is_active | is_active | 1       | const | 3747543 | Using where |
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+

Теперь, если я скажу MySQL игнорировать индекс is_active, запрос происходит мгновенно.

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10;
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | t     | range | PRIMARY       | PRIMARY | 4       | NULL | 1597518 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+

Теперь, что действительно странно, так это то, что если я заставлю MySQL использовать индекс is_active, запрос снова произойдет мгновенно!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows    | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+
|  1 | SIMPLE      | t     | range | is_active     |is_active| 5       | NULL | 1866730 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+

Я просто не понимаю этого поведения. В индексе is_active строки должны быть отсортированы по is_active, за которым следует id. Я использую столбцы «is_active» и «id» в своем запросе, поэтому кажется, что для поиска идентификаторов нужно всего лишь сделать несколько прыжков по дереву, а затем использовать эти идентификаторы для получения заголовков из таблицы.

Что происходит?

РЕДАКТИРОВАТЬ: Больше информации о том, что я делаю:

  • Кеш запросов отключен
  • Запуск OPTIMIZE TABLE и ANALYZE TABLE не дал эффекта
  • 6 620 372 строки имеют для is_active значение True. В 874 714 строках для параметра is_active установлено значение False.
  • Использование FORCE INDEX (is_active) еще раз ускоряет запрос.
  • MySQL версия 5.1.54

Ответы [ 3 ]

7 голосов
/ 29 сентября 2011

Похоже, что MySQL принимает неверное решение о том, как использовать индекс.

Из этого плана запросов он показывает, что мог использовать либо PRIMARY, либо индекс is_active, и выбрал is_active вПорядок сужения по track.is_active первым.Однако он использует только первый столбец индекса (track.is_active).Это дает 3747543 результатов, которые затем должны быть отфильтрованы и отсортированы.

Если бы он выбрал индекс PRIMARY, он мог бы сузить до 1597518 строк, используя индекс, и они были бы получены в порядкеtrack.id, который не требует дальнейшей сортировки.Это было бы быстрее.

Новая информация:

В третьем случае, когда вы используете FORCE INDEX , MySQL использует индекс is_active, но теперь вместо использования толькопервый столбец, он использует оба столбца (см. key_len).Поэтому теперь он может сужаться по is_active и сортировать и фильтровать по id с использованием того же индекса, а поскольку is_active является единственной константой, ORDER BY удовлетворяется вторым столбцом (то есть строки из одной ветви индекса ужев отсортированном порядке).Кажется, это даже лучший результат, чем использование PRIMARY - и, возможно, то, что вы изначально хотели, не так ли?

Я не знаю, почему он не использовал оба столбца этого индекса без FORCE INDEX,если запрос не изменился тонким способом между ними.Если нет, то я откажусь от принятия MySQL неправильных решений.

1 голос
/ 29 сентября 2011

Я думаю, что ускорение связано с вашим предложением where. Я предполагаю, что это только получение небольшого подмножества строк во всей большой таблице. Быстрее выполнить сканирование таблицы полученных данных для is_active для небольшого подмножества, чем выполнить фильтрацию через большой индексный файл. Обход индекса одного столбца намного быстрее, чем обход объединенного индекса.

0 голосов
/ 29 сентября 2011

Мало что можно попробовать:

  • Сделайте ОПТИМИЗАЦИЮ и ПРОВЕРЬТЕ свою таблицу, чтобы mysql пересчитал значения индекса
  • посмотрите на http://dev.mysql.com/doc/refman/5.1/en/index-hints.html - вы можете сказать mysql, чтобы он выбирал правильный индекс в разных случаях
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...