Почему MYSQL с большим смещением LIMIT замедляет запрос? - PullRequest
148 голосов
/ 19 декабря 2010

Сценарий вкратце: таблица с более чем 16 миллионами записей [размером 2 ГБ].Чем выше смещение LIMIT с помощью SELECT, тем медленнее становится запрос при использовании ORDER BY * primary_key *

Так что

SELECT * FROM large ORDER BY `id`  LIMIT 0, 30 

занимает намного меньше, чем

SELECT * FROM large ORDER BY `id` LIMIT 10000, 30 
* 1008только заказы 30 записей и так же в любом случае.Так что это не накладные расходы от ORDER BY.
Теперь при получении последних 30 строк это занимает около 180 секунд.Как я могу оптимизировать этот простой запрос?

Ответы [ 5 ]

183 голосов
/ 05 июня 2013

У меня была точно такая же проблема. Учитывая тот факт, что вы хотите собрать большой объем этих данных, а не определенный набор из 30, вы, вероятно, будете выполнять цикл и увеличивать смещение на 30.

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

  1. Содержит последний идентификатор набора данных (30) (например, lastId = 530)
  2. Добавить условие WHERE id > lastId limit 0,30

Так что вы всегда можете иметь нулевое смещение. Вы будете поражены улучшением производительности.

168 голосов
/ 21 декабря 2010

Обычно старшие смещения замедляют запрос, поскольку запрос должен отсчитывать первые OFFSET + LIMIT записи (и принимать только LIMIT из них). Чем выше это значение, тем дольше выполняется запрос.

Запрос не может перейти прямо к OFFSET, потому что, во-первых, записи могут быть разной длины, а во-вторых, могут быть пропуски в удаленных записях. Он должен проверять и считать каждую запись на своем пути.

Предполагая, что id является PRIMARY KEY таблицы MyISAM, вы можете ускорить его с помощью этого трюка:

SELECT  t.*
FROM    (
        SELECT  id
        FROM    mytable
        ORDER BY
                id
        LIMIT 10000, 30
        ) q
JOIN    mytable t
ON      t.id = q.id

См. Эту статью:

16 голосов
/ 21 декабря 2010

MySQL не может перейти непосредственно к 10000-й записи (или 80000-му байту, как вы предлагаете), потому что он не может предположить, что он упакован / упорядочен таким образом (или что он имеет непрерывные значения от 1 до 10000). Хотя в действительности это может быть так, MySQL не может предположить, что нет дыр / пробелов / удаленных идентификаторов.

Итак, как заметил Бобс, MySQL должен будет извлечь 10000 строк (или пройти 10000-ые записи индекса на 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 с (то есть пробелов).

5 голосов
/ 01 октября 2015

Я нашел интересный пример для оптимизации запросов SELECT ORDER BY id LIMIT X, Y. У меня 35 миллионов строк, поэтому мне понадобилось 2 минуты, чтобы найти диапазон строк.

Вот трюк:

select id, name, address, phone
FROM customers
WHERE id > 990
ORDER BY id LIMIT 1000;

Просто поместите WHERE с последним идентификатором, который вы получили, чтобы увеличить производительность. Для меня это было от 2 минут до 1 секунды:)

Другие интересные трюки здесь: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

Это также работает со строками

5 голосов
/ 19 декабря 2010

Отнимающая много времени часть двух запросов извлекает строки из таблицы.Логически говоря, в версии LIMIT 0, 30 необходимо извлечь только 30 строк.В версии LIMIT 10000, 30 оценивается 10000 строк и возвращается 30 строк.В процессе чтения данных можно провести некоторую оптимизацию, но учтите следующее:

Что если в запросах есть предложение WHERE?Механизм должен вернуть все подходящие строки, а затем отсортировать данные и, наконец, получить 30 строк.

Также рассмотрим случай, когда строки не обрабатываются в последовательности ORDER BY.Все подходящие строки должны быть отсортированы, чтобы определить, какие строки возвращать.

...