Scenariusz w skrócie: tabela z ponad 16 milionami rekordów [rozmiar 2 GB]. Im większe przesunięcie LIMIT z SELECT, tym wolniejsze staje się zapytanie, gdy używa się ORDER BY * primary_key *
Więc
SELECT * FROM large ORDER BY `id` LIMIT 0, 30
zajmuje znacznie mniej niż
SELECT * FROM large ORDER BY `id` LIMIT 10000, 30
To zamawia tylko 30 płyt i tak samo. Więc to nie jest narzut z ORDER BY.
Teraz pobieranie ostatnich 30 wierszy zajmuje około 180 sekund. Jak mogę zoptymalizować to proste zapytanie?
mysql
performance
sql-order-by
limit
Rahman
źródło
źródło
Odpowiedzi:
To normalne, że wyższe przesunięcia spowalniają zapytanie, ponieważ zapytanie musi odliczać pierwsze
OFFSET + LIMIT
rekordy (i pobierać tylkoLIMIT
z nich). Im wyższa jest ta wartość, tym dłużej trwa zapytanie.Zapytanie nie może przejść od razu,
OFFSET
ponieważ po pierwsze rekordy mogą mieć różną długość, a po drugie, mogą istnieć luki w usuniętych rekordach. Musi sprawdzić i policzyć każdy rekord w drodze.Zakładając, że
id
jestPRIMARY KEY
zMyISAM
tabeli, można ją przyspieszyć stosując ten trick:Zobacz ten artykuł:
źródło
ORDER BY
lub indeks obejmuje wszystkie potrzebne pola, nie potrzebujesz tego obejścia.postgresql
. To jest odpowiedź specyficzna dla MySQL.Sam miałem ten sam problem. Biorąc pod uwagę fakt, że chcesz zebrać dużą ilość tych danych, a nie konkretny zestaw 30, prawdopodobnie uruchomisz pętlę i zwiększysz przesunięcie o 30.
Zamiast tego możesz zrobić:
WHERE id > lastId limit 0,30
Więc zawsze możesz mieć przesunięcie ZERO. Będziesz zaskoczony poprawą wydajności.
źródło
MySQL nie może przejść bezpośrednio do 10000. rekordu (lub 80000 bajtu, jak sugerujesz), ponieważ nie może założyć, że jest tak spakowany / uporządkowany (lub że ma ciągłe wartości od 1 do 10000). Chociaż w rzeczywistości może tak być, MySQL nie może zakładać, że nie ma dziur / luk / usuniętych identyfikatorów.
Tak więc, jak zauważył bobs, MySQL będzie musiał pobrać 10000 wierszy (lub przejść przez 10000-te pozycje indeksu
id
) przed znalezieniem 30 do zwrócenia.EDYCJA : Aby zilustrować mój punkt widzenia
Zauważ, że chociaż
byłby wolny (er) ,
byłby szybszy (er) i zwróciłby te same wyniki pod warunkiem, że nie ma brakujących
id
s (tj. luk).źródło
Znalazłem ciekawy przykład optymalizacji zapytań SELECT ORDER BY id LIMIT X, Y. Mam 35 milionów wierszy, więc znalezienie ich zajęło 2 minuty.
Oto sztuczka:
Po prostu wpisz GDZIE z ostatnim identyfikatorem, który dostałeś, aby znacznie zwiększyć wydajność. U mnie było to od 2 minut do 1 sekundy :)
Inne ciekawe sztuczki tutaj: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/
Działa również ze sznurkami
źródło
Czasochłonną częścią tych dwóch zapytań jest pobieranie wierszy z tabeli. Logicznie rzecz biorąc, w tej
LIMIT 0, 30
wersji trzeba pobrać tylko 30 wierszy. W tejLIMIT 10000, 30
wersji ocenianych jest 10000 wierszy i zwracanych jest 30 wierszy. Proces odczytu danych może zostać zoptymalizowany, ale weź pod uwagę następujące kwestie:A co by było, gdybyś miał w zapytaniach klauzulę WHERE? Silnik musi zwrócić wszystkie wiersze, które się kwalifikują, a następnie posortować dane i ostatecznie pobrać 30 wierszy.
Weź również pod uwagę przypadek, w którym wiersze nie są przetwarzane w sekwencji ORDER BY. Wszystkie kwalifikujące się wiersze muszą zostać posortowane, aby określić, które wiersze mają zostać zwrócone.
źródło
Dla zainteresowanych porównaniem i liczbami :)
Eksperyment 1: zbiór danych zawiera około 100 milionów wierszy. Każdy wiersz zawiera kilka BIGINT, TINYINT, a także dwa pola TEXT (celowo) zawierające około 1k znaków.
SELECT * FROM post ORDER BY id LIMIT {offset}, 5
SELECT t.* FROM (SELECT id FROM post ORDER BY id LIMIT {offset}, 5) AS q JOIN post t ON t.id = q.id
... WHERE id>xxx LIMIT 0,5
nie pojawia się tutaj, ponieważ powinna to być stała czasowa.Eksperyment 2: Podobna rzecz, z tą różnicą, że jeden wiersz ma tylko 3 BIGINT.
źródło