Я диагностирую прерывистый медленный запрос и обнаружил странное поведение в 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) как сами данные могут влиять на ключевую стратегию? И какой аспект данных, видя, что количество и распространение в индексах кажется типичным.