LIMIT 1 очень медленный, для конкретных записей, используя разные клавиши
Я диагностирую прерывистый медленный запрос и обнаружил странное поведение в MySQL, которое я не могу объяснить. Он выбирает другую, неоптимальную стратегию ключа для одного конкретного случая, только при выполнении LIMIT 1
.
Таблица (некоторые удаленные столбцы данных без ссылок удалены для краткости)
CREATE TABLE `ch_log` (
`cl_id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`cl_unit_id` INT(11) NOT NULL DEFAULT '0',
`cl_date` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
`cl_type` CHAR(1) NOT NULL DEFAULT '',
`cl_data` TEXT NOT NULL,
`cl_event` VARCHAR(255) NULL DEFAULT NULL,
`cl_timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`cl_record_status` CHAR(1) NOT NULL DEFAULT 'a',
PRIMARY KEY (`cl_id`),
INDEX `cl_type` (`cl_type`),
INDEX `cl_date` (`cl_date`),
INDEX `cl_event` (`cl_event`),
INDEX `cl_unit_id` (`cl_unit_id`),
INDEX `log_type_unit_id` (`cl_unit_id`, `cl_type`),
INDEX `unique_user` (`cl_user_number`, `cl_unit_id`)
)
ENGINE=InnoDB
AUTO_INCREMENT=419582094;
Это запрос, который работает только медленно для одного конкретного cl_unit_id
:
EXPLAIN
SELECT *
FROM `ch_log`
WHERE `ch_log_type` ='I' and ch_log_event = 'G'
AND cl_unit_id=1234
ORDER BY cl_date DESC
LIMIT 1;
id|select_type|table |type |possible_keys |key |key_len|ref|rows|Extra
1 |SIMPLE |ch_log|index|cl_type,cl_event,cl_unit_id,log_type_unit_id|cl_date|8 |\N |5295|Using where
Для всех остальных значений cl_unit_id
он использует клавишу log_type_unit_id
, которая намного быстрее.
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra
1 |SIMPLE |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5 |const,const|3804|Using where; Using filesort
- Все запросы занимают около 0,01 секунды.
- Запрос "медленный блок" занимает 10-15 минут!
Я не вижу ничего странного в данных для этой "единицы":
- У блока 1234 только 6 записей типа я и события G.
- Другие устройства имеют гораздо больше.
- Единица 1234 имеет всего 32 000 журналов, что типично.
- сами данные являются нормальными, не более или более старыми.
- В базе данных содержится около 3000 единиц, которые представляют собой записи в устройствах. Cl_unit_id - это их уникальный PK (хотя и не ограничение).
Общая информация
- Всего 30 миллионов записей, около 12 ГБ.
- mysql 5.1.69-log
- Centos 64bit
- Данные постепенно меняются (30 м = 3 месяца журналов), но я не знаю, произошло ли это раньше.
Вещи, которые я пробовал, и могут "решить" проблему с помощью:
-
Удаление LIMIT 1
- запрос выполняется в миллисекундах и возвращает данные.
-
Переход на LIMIT 2
или другие комбинации, например. 2,3 - работает в миллисекундах.
-
Добавление указателя индекса - решает его:
FROM `ch_log` USE INDEX (log_type_unit_id)
но... Я не хочу жестко закодировать это в приложении.
-
Добавление второго порядка по первичному ключу также "решает" его:
ORDER BY cl_id, cl_date DESC
объясняя:
id|select_type|table |type|possible_keys |key |key_len|ref |rows|Extra
1 |SIMPLE |ch_log|ref |ch_log_type,ch_log_event,ch_log_unit_id,log_type_unit_id|log_type_unit_id|5 |const,const|6870|Using where
который немного отличается от намеченного типа, с большим количеством зарегистрированных записей (6000), но все еще работает в течение 10 миллисекунд.
Снова я могу это сделать, но мне не нравятся побочные эффекты, которые я не понимаю.
Итак, я думаю, что мой главный вопрос:
a) почему это происходит только для LIMIT 1
?
b) как сами данные могут влиять на ключевую стратегию? И какой аспект данных, видя, что количество и распространение в индексах кажется типичным.
Ответы
Ответ 1
Mysql выберет план объяснения и использует разные индексы в зависимости от того, что, по его мнению, является статистически лучшим выбором. Для всех ваших первых вопросов это ответ:
- Удаление
LIMIT 1
- запрос выполняется в миллисекундах и возвращает данные.
и → Да, проверьте это, план объяснения хорош.
- Переход на
LIMIT 2
или другие комбинации, например. 2,3 - работает в миллисекундах. → то же самое. Оптимизатор выбирает другой индекс, потому что внезапно ожидаемые чтения блоков становятся вдвое большими, чем при LIMIT 1
(это только одна возможность).
- Добавление подсказки индекса решает ее → Конечно, вы заставляете хороший план объяснений
- Добавление второго порядка по первичному ключу также "решает" его → да, потому что по совпадению результат - лучший план объяснения.
Теперь это отвечает только на половину вопросов.
a) почему это происходит только для LIMIT 1?
На самом деле это происходит не только из-за LIMIT 1
, но из-за
- Перераспределение статистики данных (ориентирует решения оптимизатора)
- Предложение
ORDER BY DESC
. Попробуйте ORDER BY ... ASC
, и вы также увидите улучшение.
Это явление прекрасно понимается. читать.
Одно из принятых решений (ниже в статье) - заставить индекс так же, как и вы. Да, иногда это оправдано. В противном случае этот намек был бы полностью уничтожен давно. Роботы не всегда могут быть идеальными: -)
b) как сами данные могут влиять на ключевую стратегию? И что аспект данных, видя как количество и распространение в индексах кажется типичным.
Вы сказали это, распространение - это то, что обычно трахается. Не только оптимизатор может просто принять неправильное решение с точной статистикой, но он также может быть полностью отключен только потому, что дельта на таблице находится под 1/16-м общее количество строк...