Почему удаление этого индекса в MySQL ускоряет мой запрос 100x?

У меня есть следующая таблица 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 |
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+

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

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

EDIT: больше информации о том, что я делаю:

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

Ответ 1

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

Из этого плана запроса он показывает, что он мог использовать либо PRIMARY, либо is_active index, и он выбрал 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, приняв плохие решения.

Ответ 2

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

Ответ 3

Несколько вещей, которые вы могли бы попробовать:

  • Сделайте OPTIMIZE и CHECK в своей таблице, так что mysql будет пересчитывать значения индекса
  • посмотрите http://dev.mysql.com/doc/refman/5.1/en/index-hints.html - вы можете указать mysql выбрать правильный индекс в разных случаях