#mysql #indexing
#mysql #индексирование
Вопрос:
У меня есть следующая таблица 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.
- Использование ПРИНУДИТЕЛЬНОГО ИНДЕКСА (is_active) снова ускоряет запрос.
- Версия MySQL 5.1.54
Комментарии:
1. Вы очищаете кеш перед сравнительным анализом, верно?
2. Также убедитесь, что статистика таблицы актуальна, а индексы перестроены. (Однако это делается в MySQL 😉
3. Что произойдет, если вы измените условия WHERE?
where (track.id > 5580702 and track.is_active=1)
Ответ №1:
Похоже, MySQL принимает неправильное решение о том, как использовать индекс.
Из этого плана запроса видно, что он мог использовать либо ОСНОВНОЙ, либо индекс is_active , и он выбрал is_active, чтобы сначала сузить его по track.is_active . Однако он использует только первый столбец индекса (track.is_active). Это дает 3747543 результатов, которые затем должны быть отфильтрованы и отсортированы.
Если бы он выбрал ОСНОВНОЙ индекс, он смог бы сузить до 1597518 строк, используя индекс, и они были бы извлечены в порядке track.id уже, что не должно требовать дальнейшей сортировки. Это было бы быстрее.
Новая информация:
В третьем случае, когда вы используете ПРИНУДИТЕЛЬНЫЙ ИНДЕКС, MySQL использует индекс is_active, но теперь вместо того, чтобы использовать только первый столбец, он использует оба столбца (см. key_len ). Поэтому теперь он может сужаться по is_active и сортировать и фильтровать по идентификатору, используя тот же индекс, а поскольку is_active является единственной константой, порядок ПО выполняется вторым столбцом (т. Е. Строки из одной ветви индекса уже отсортированы). Кажется, это даже лучший результат, чем использование PRIMARY — и, вероятно, то, что вы предполагали в первую очередь, верно?
Я не знаю, почему он не использовал оба столбца этого индекса без ПРИНУДИТЕЛЬНОГО ИНДЕКСА, если только запрос не изменился незначительным образом между ними. Если нет, я бы списал это на то, что MySQL принимает неправильные решения.
Комментарии:
1. Конечно, если вы знаете лучше, чем MySQL, вы всегда можете использовать USE INDEX () , чтобы подсказать, какой индекс он должен предпочесть. Вы также можете попробовать ANALYZE TABLE , чтобы дать MySQL возможность разобраться с этим самостоятельно, что иногда может сработать.
2. Если я использую ПРИНУДИТЕЛЬНЫЙ ИНДЕКС (is_active), запрос выполняется мгновенно (см. Последние правки). Есть идеи?
3. Я не уверен — возможно, какой-то кеш? Может быть, добавить для этого вывод EXPLAIN? Вы получаете тот же результат в том же порядке?
4. Я добавил вывод EXPLAIN в вопросе. Кэш запросов отключен. Попытался перезапустить MySQL, чтобы очистить любые другие кеши, получил тот же результат.
5. Новое ОБЪЯСНЕНИЕ показывает, что теперь он использует оба столбца индекса is_active, а не только один, как раньше. Я не совсем понимаю, почему. Я обновил свой ответ дополнительной информацией.
Ответ №2:
Я думаю, что ускорение связано с вашим предложением where. Я предполагаю, что он извлекает только небольшое подмножество строк во всей большой таблице. Быстрее выполнить сканирование таблицы полученных данных для is_active в небольшом подмножестве, чем выполнять фильтрацию через большой индексный файл. Обход индекса с одним столбцом намного быстрее, чем обход комбинированного индекса.
Ответ №3:
Несколько вещей, которые вы могли бы попробовать:
- Оптимизируйте и ПРОВЕРЬТЕ свою таблицу, чтобы mysql пересчитал значения индекса
- взгляните на http://dev.mysql.com/doc/refman/5.1/en/index-hints.html — вы можете указать mysql выбрать правильный индекс в разных случаях