Почему 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.

Итак, что вы можете сделать вместо этого:

  1. удерживайте последний идентификатор набора данных (30) (например, lastId = 530)
  2. добавить условие 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), и вернул бы те же результаты при условии, что нет отсутствующих ids (т. е. пробелы).


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

Он также работает со строками