Почему MySQL higher LIMIT offset замедляет запрос?
сценарий вкратце: таблица с более чем 16 миллионами записей [размером 2 ГБ]. Чем выше смещение предела с SELECT, тем медленнее становится запрос при использовании ORDER BY * primary_key*
Так
SELECT * FROM large ORDER BY `id` LIMIT 0, 30
занимает гораздо меньше, чем
SELECT * FROM large ORDER BY `id` LIMIT 10000, 30
это только заказы 30 записей и тот же eitherway. Так что это не накладные расходы от ORDER BY.
Теперь при получении последних 30 строк требуется около 180 секунд. Как я могу оптимизировать это просто запрос?
5 ответов
это нормально, что более высокие смещения замедляют запрос, так как запрос должен отсчитывать первый OFFSET + LIMIT
записи (и взять только LIMIT
из них). Чем выше это значение, тем дольше выполняется запрос.
запрос не может перейти право OFFSET
потому что, во-первых, записи могут быть разной длины, и, во-вторых, могут быть пробелы в удаленных записях. Он должен проверять и считать каждую запись на своем пути.
предполагая, что id
это PRIMARY KEY
of a MyISAM
таблица, вы можете ускорить его, используя этот трюк:
SELECT t.*
FROM (
SELECT id
FROM mytable
ORDER BY
id
LIMIT 10000, 30
) q
JOIN mytable t
ON t.id = q.id
посмотреть в этой статье:
У меня была точно такая же проблема. Учитывая тот факт, что вы хотите собрать большой объем этих данных, а не конкретный набор из 30, вы, вероятно, будете запускать цикл и увеличивать смещение на 30.
Итак, что вы можете сделать вместо этого:
- удерживайте последний идентификатор набора данных (30) (например, lastId = 530)
- добавить условие
WHERE id > lastId limit 0,30
таким образом, вы всегда можете иметь нулевое смещение. Вы будете поражены представлением улучшение.
MySQL не может перейти непосредственно к 10000th записи (или 80000th байт, как ты думаешь) потому что он не может предположить, что он упакован/заказать подобное (или что она имеет непрерывные значения от 1 до 10000). Хотя это может быть так на самом деле, MySQL не может предположить, что нет отверстий/пробелов/удаленных идентификаторов.
Итак, как отметил Бобс, MySQL должен будет получить 10000 строк (или пройти через 10000th записи индекса на id
), прежде чем найти 30 вернуться.
редактировать : чтобы проиллюстрировать мою точку зрения
обратите внимание, что хотя
SELECT * FROM large ORDER BY id LIMIT 10000, 30
будет медленно (er),
SELECT * FROM large WHERE id > 10000 ORDER BY id LIMIT 30
будет быстрый (er), и вернул бы те же результаты при условии, что нет отсутствующих id
s (т. е. пробелы).
трудоемкой частью двух запросов является извлечение строк из таблицы. Логически говоря, в LIMIT 0, 30
версия, нужно извлечь только 30 строк. В LIMIT 10000, 30
version, 10000 строк вычисляются и возвращаются 30 строк. Там может быть некоторая оптимизация может быть сделано мой процесс чтения данных, но рассмотрим следующее:
что, если бы у вас было предложение WHERE в запросах? Обработчик должен вернуть все строки, которые соответствуют требованиям, а затем отсортировать данные и, наконец, получить 30 строк.
также рассмотрим случай, когда строки не обрабатываются в порядке последовательности. Все квалифицирующие строки должны быть отсортированы, чтобы определить, какие строки возвращать.
Я нашел интересный пример оптимизации порядка выбора запросов по ID LIMIT X, Y. У меня 35 миллионов строк, поэтому потребовалось 2 минуты, чтобы найти диапазон строк.
вот трюк :
select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;
просто поместите, где с последним идентификатором вы получили увеличение производительности. Для меня это было от 2 минут до 1 секунды:)
другие интересные трюки : http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/
Он также работает со строками