#mysql #performance #filesort
#mysql #Производительность #сортировка файлов
Вопрос:
Привет, у меня очень медленный запрос MySQL. Я уверен, что все, что мне нужно сделать, это добавить правильный индекс, но все, что я пробую, не работает.
Запрос является:
SELECT DATE(DateTime) as 'SpeedDate', avg(LoadTime) as 'LoadTime'
FROM SpeedMonitor
GROUP BY Date(DateTime);
Объяснение запроса таково:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE SpeedMonitor ALL 7259978 Using temporary; Using filesort
И структура таблицы:
CREATE TABLE `SpeedMonitor` (
`SMID` int(10) unsigned NOT NULL auto_increment,
`DateTime` datetime NOT NULL,
`LoadTime` double unsigned NOT NULL,
PRIMARY KEY (`SMID`)
) ENGINE=InnoDB AUTO_INCREMENT=7258294 DEFAULT CHARSET=latin1;
Любая помощь была бы высоко оценена.
Комментарии:
1. Подождите, это медленно, так как для завершения требуется много времени?
2. да, потому что это копирование во временную таблицу
3. Я ответил вам, но я спрашиваю, сколько записей в этой таблице
4. в сообщении explain говорится, что существует 7259978
5. Я не думаю, что дизайн вашей таблицы имеет большой смысл. Из-за этого вы просите запрос сгруппировать по полю datetime, которое включает миллисекунды, чтобы он мог вычислять среднее значение поля time? Сколько записей будет иметь точно такую же временную метку даты? Я предполагаю, что вы ищете среднее время загрузки в день, и в этом случае вы хотели бы иметь группу только по дате? Я прав?
Ответ №1:
Вы просто запрашиваете два столбца в своем запросе, поэтому индексы могут / должны идти туда:
- Дата и время
- Время загрузки
Другим способом ускорить ваш запрос может быть разделение поля DateTime на два: дата и время.
Таким образом, база данных может группироваться непосредственно по полю даты вместо вычисления даты (…).
ОТРЕДАКТИРОВАНО:
Если вы предпочитаете использовать триггер, создайте новый столбец (DATE) и назовите его newdate, и попробуйте с этим (я не могу попробовать это сейчас, чтобы проверить, правильно ли это):
CREATE TRIGGER upd_check BEFORE INSERT ON SpeedMonitor
FOR EACH ROW
BEGIN
SET NEW.newdate=DATE(NEW.DateTime);
END
ОТРЕДАКТИРОВАНО СНОВА:
Я только что создал базу данных с той же таблицей speedmonitor, заполненной примерно 900 000 записями.
Затем я запустил запросы ELECT newdate,AVG(LoadTime) loadtime FROM speedmonitor GROUP BY newdate
, и это заняло около 100 секунд!!
Удаление индекса в поле newdate (и очистка кэша с помощью RESET QUERY CACHE
и FLUSH TABLES
), тот же запрос занял 0,6 секунды!!!
Просто для сравнения: запрос SELECT DATE(DateTime),AVG(LoadTime) loadtime FROM speedmonitor GROUP BY DATE(DateTime)
занял 0,9 секунды.
Итак, я полагаю, что индекс в newdate не подходит: удалите его.
Сейчас я собираюсь добавить столько записей, сколько смогу, и снова протестировать два запроса.
ОКОНЧАТЕЛЬНОЕ РЕДАКТИРОВАНИЕ:
Удаление индексов в столбцах newdate и DateTime, имеющих 8 миллионов записей в таблице speedmonitor, вот результаты:
- выбор и группировка в столбце newdate: 7,5с
- выбор и группировка по полю ДАТЫ (DateTime): 13,7 с
Я думаю, что это хорошее ускорение.
Время, затрачиваемое на выполнение запроса в командной строке mysql.
Комментарии:
1. 2 отдельных индекса или 1 объединенный индекс?
2. разделение полей на данный момент на самом деле не является вариантом
3. @Jonny Shaw: ваша база данных все еще используется? Есть ли активное приложение, записывающее в него? Количество записей растет?
4. да, количество записей увеличивается примерно на 300 000 каждый день.
5. @Jonny Shaw: ваш движок через некоторое время перестанет выполнять этот запрос. Вы действительно не можете изменить tabledef и разделить поле DateTime? Сколько раз вам нужно выполнить этот запрос?
Ответ №2:
Проблема в том, что вы используете функцию в своем GROUP BY
предложении, поэтому MySQL должен вычислять выражение Date(DateTime)
для каждой записи, прежде чем он сможет сгруппировать результаты. Я бы предложил добавить вычисляемое поле для Date(DateTime)
, которое вы могли бы затем проиндексировать и посмотреть, поможет ли это вашей производительности.
Комментарии:
1. что вы подразумеваете под вычисляемым полем?
2. Да, вы абсолютно правы, я уже предлагал это… но если эта база данных используется (я имею в виду, не завершена), он должен изменить логику внутри приложения, записывающего в db. Я не знаю, сможет ли он…
3. @Jonny Shaw: я думаю, он имеет в виду, что вам следует добавить новый столбец, в котором вы должны хранить только часть ДАТЫ поля DateTime
4. база данных в настоящее время находится в активной среде и заполняется очень быстро. так что это не то, что можно было бы легко изменить.
5. Оказывается, MySQL не поддерживает ни вычисляемые поля, ни индексы в представлениях, так что, похоже, у вас есть только варианты добавить поле даты и заполнить его самостоятельно в коде или с помощью триггера, или переключиться на другую платформу базы данных.
Ответ №3:
Я надеюсь, вы позволите мне указать, что перед запуском в производство таблицы с миллионами записей вам следует серьезно подумать о том, как эти данные будут использоваться, и соответствующим образом спланировать.
Что происходит прямо сейчас, так это то, что ваш запрос не может использовать какие-либо индексы и, следовательно, сканирует всю таблицу, создавая ответ. Не самый быстрый способ работы с относительно большими таблицами.
Вам нужно учитывать некоторые моменты, если вы хотите перейти к лучшему состоянию:
- Насколько быстро он собирает данные?
- Сколько истории вам нужно?
- Насколько детализированы ваши требования к отчетности?
- Можете ли вы приостановить ведение журнала для внесения изменений в таблицу?
Если ответ «Нет» на последний вопрос, вы всегда можете создать новую таблицу / решение и начать записывать туда записи … импортируя старые данные, если / по мере необходимости.
Важна детализация отчетов, поскольку вы могли бы, например, сжать данные за день в 24 записи. Загрузите текущий день в таблицу загрузки без индекса, а затем обработайте его на следующий день в усредненных значениях за час. Назовите каждую загружаемую таблицу на основе даты выборки, и вы сможете удалять старые таблицы по мере обработки.
Конечно, hourly может быть недостаточно детализированным.
В зависимости от ваших потребностей в хранении вы можете рассмотреть какой-либо тип секционированного хранилища. Это может позволить вам запрашивать подмножества выборочных данных и просто удалять или архивировать старые разделы, когда они уже недостаточно актуальны, чтобы быть актуальными.
В любом случае, вы, похоже, находитесь на грани создания какого-то типа системы массовой выборки, отчетности и / или мониторинга (особенно, если вы сообщали о множестве сайтов или страниц с разными характеристиками). Возможно, вы захотите приложить некоторые усилия для разработки этого, чтобы оно соответствовало вашим потребностям … 😉