Próbuję zindeksować blogentries
bazę danych w celu zwiększenia wydajności, ale znalazłem problem.
Oto struktura:
CREATE TABLE IF NOT EXISTS `blogentries` (
`id_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
`entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
`date_id` int(11) NOT NULL,
PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;
Zapytanie takie jak poniżej poprawnie używa indeksu:
EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- + | id | wybierz typ | stół | typ | możliwe klucze | klucz | key_len | ref | rzędy | Extra | + ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- + | 1 | PROSTE | blogentries | indeks | NULL | PODSTAWOWE | 114 | NULL | 126 | Korzystanie z indeksu | + ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
Jednak gdy dodam zapytanie entry_id
do SELECT
zapytania, używa on pliku
EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- + | id | wybierz typ | stół | typ | możliwe klucze | klucz | key_len | ref | rzędy | Extra | + ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- + | 1 | PROSTE | blogentries | WSZYSTKIE | NULL | NULL | NULL | NULL | 126 | Korzystanie z sortowania plików | + ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
Zastanawiałem się, dlaczego tak się dzieje i jak mogę tego uniknąć? Czy to z powodu VarChar
i należy to zmienić na coś innego?
Usiłuję, aby wszystkie moje zapytania korzystały z indeksu, ponieważ uruchamiam wysokie wartości Handler_read_rnd
i Handler_read_rnd_next
wartości.
Jeśli potrzebujesz innych informacji, też mogę je opublikować.
WHERE 1=1
do drugiego zapytania.SELECT @@sort_buffer_size
)?Odpowiedzi:
Ponieważ nie masz
WHERE
klauzuli w żadnym zapytaniu, zwracasz wszystkie wiersze w obu przypadkach, więc sądzę, że użycie lub niewykorzystanie indeksu miałoby bardzo niewielki wpływ na wydajność w tych przykładach.źródło
ORDER BY
?varchar(5000)
.Jak udokumentowano w części
ORDER BY
Optymalizacja :W swoim artykule na blogu Czym dokładnie jest read_rnd_buffer_size , Peter Zaitsev wyjaśnia:
Sugeruje to, że
max_length_for_sort_data
jest to limit całkowitego rozmiaru wybieranych kolumn, powyżej któregofilesort
zostanie zastosowane a zamiast sortowania opartego na indeksie.W twoim przypadku wybranie
entry_id
(5002 bajtów) przejmuje całkowity rozmiar powyżej domyślnej wartości 1KiB tej zmiennej i dlategofilesort
jest używane. Aby podnieść limit do 8 KB, możesz:źródło
Otrzymałeś tutaj wiele interesujących odpowiedzi, ale nikt dokładnie nie odpowiedział na pytanie - dlaczego tak się dzieje? Jak rozumiem, gdy zapytanie SELECT zawiera dane o zmiennej długości w MySQL i nie ma indeksu, który pasowałby do WSZYSTKICH żądanych kolumn, zawsze użyje sortowania plików. Rozmiar danych nie jest tutaj szczególnie istotny. Trudno znaleźć bezpośrednią odpowiedź na to pytanie w dokumentacji MySQL, ale tutaj jest dobry post na blogu, w którym ktoś ma bardzo podobny problem do twojego.
Zobacz także: 10 porad dotyczących optymalizacji zapytań MySQL (które nie są do kitu) .
Jeśli więc możliwe jest posiadanie indeksu na entry_id, możesz go dodać i wszystko było ustawione. Wątpię jednak, czy jest to opcja, więc co robić?
Czy powinieneś coś z tym zrobić, to osobne pytanie. Ważne jest, aby wiedzieć, że „plik plików” jest źle nazwany w MySQL - tak naprawdę jest to tylko nazwa algorytmu używanego do sortowania tego konkretnego zapytania, aw wielu przypadkach sortowanie faktycznie nastąpi w pamięci. Jeśli nie spodziewasz się, że ten stół będzie się powiększał, prawdopodobnie nie jest to wielka sprawa.
Z drugiej strony, jeśli ta tabela będzie miała milion wierszy, możesz mieć problem. Jeśli potrzebujesz obsługi podziału stron na zapytania w tej tabeli, możesz mieć naprawdę poważny problem z wydajnością. W takim przypadku podzielenie danych o zmiennej długości na nową tabelę i wykonanie JOIN w celu ich odzyskania jest poprawną optymalizacją do rozważenia.
Oto kilka innych odpowiedzi na temat SO, które omawiają to pytanie:
źródło
filesort
najwyraźniej nie został w tym przypadku użyty. Myślę również, że nawet samo sortowanie małej tabeli w pamięci może okazać się niedopuszczalnym spadkiem wydajności: np. Jeśli zapytanie jest wykonywane bardzo często (a tabela zmienia się, tak że nie można użyć pamięci podręcznej).Spróbuj dodać
WHERE
klauzulę do swoich zapytań.http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
źródło
ORDER BY
nie pasuje indeks dokładnie, więc nie ma potrzeby, aby miećWHERE
klauzulę.O ile mi wiadomo, varchar może przechowywać maksymalnie 8000 bajtów, czyli około 4000 znaków. Tak więc 5000 wydaje się przekraczać limit przechowywania, aw tym przypadku prawdopodobnie powód, dla którego sortowanie jest pomieszane.
"varchar [(n | max)] Dane znakowe o zmiennej długości, inne niż Unicode. n może mieć wartość od 1 do 8 000. max oznacza, że maksymalny rozmiar pamięci to 2 ^ 31-1 bajtów. Rozmiar pamięci to rzeczywisty długość wprowadzonych danych + 2 bajty. Podane dane mogą mieć długość 0 znaków. Synonimy SQL-2003 dla varchar różnią się znakami lub znakami. ”
Mam nadzieję, że to odpowiada na twoje pytanie
źródło
CHAR
iVARCHAR
typów : " Wartości w kolumnach VARCHAR są ciągi o zmiennej długości Długość może zostać określona jako wartość od 0 do 255 przed MySQL 5.0.3, oraz od 0 do 65.535 w 5.0.3 i nowszych wersjach Efektywna.. maksymalna długość aVARCHAR
w MySQL 5.0.3 i nowszych zależy od maksymalnego rozmiaru wiersza (65 535 bajtów, który jest współużytkowany przez wszystkie kolumny) i użytego zestawu znaków. ”W tabeli masz tylko 126 wierszy. Nawet jeśli każdy wiersz ma rozmiar maksymalnie około 5 KB, oznacza to, że całkowity rozmiar do odczytu z dysku to tylko około 600 KB - to nie jest dużo. Szczerze mówiąc, jest to bardzo mała ilość, prawdopodobnie mniejsza niż rozmiar pamięci podręcznej większości współczesnych dysków.
Teraz, jeśli serwer musi pobrać dane w celu wykonania zapytania, najdroższą operacją jest odczytanie ich z dysku. Ale czytanie go zgodnie z kolejnością indeksów NIE zawsze jest najszybszym sposobem, aby to zrobić, szczególnie gdy ilość danych jest tak mała.
W twoim przypadku O wiele bardziej efektywne jest odczytywanie danych całej tabeli z dysku jako pojedynczego bloku do pamięci (prawdopodobnie w ramach jednej operacji odczytu dysku lub wyszukiwania), a następnie sortowanie go w pamięci RAM, aby spełnić ORDER BY, co jest natychmiastowe w porównaniu do dysku odczyt operacji. Jeśli serwer odczytuje dane zgodnie z indeksem, musiałby wykonać do 126 operacji odczytu (ups!), Wielokrotnie przeszukując ten sam plik danych.
Innymi słowy, skanowanie sekwencyjne NIE zawsze jest złą rzeczą, a mysql niekoniecznie jest głupi. Jeśli spróbujesz zmusić mysql do korzystania z tego indeksu, najprawdopodobniej będzie działał wolniej niż skanowanie sekwencyjne, które aktualnie masz.
A powodem, dla którego BYŁO używać indeksu, gdy pole 5KB nie zostało uwzględnione, jest to, że wówczas pobrane dane nie stanowiły 99% danych w tabeli. Po dodaniu pola 5KB zapytanie musi teraz odczytać 99% danych, a tańsze jest przeczytanie całości i późniejsze posortowanie jej w pamięci.
źródło
JOIN
warunki iWHERE
klauzulach, a nieORDER BY
klauzulach.Jakiej wersji MySQL używasz?
W wersji 5.1 próbowałem skonfigurować scenariusz i zapełniłem dane zastępcze. Korzystając z podanych przez ciebie SQL, otrzymuję skan tabeli tylko za każdym razem zgodnie z EXPLAIN. Domyślnie, gdy używasz kolejności przez MYSQL, ucieka się do sortowania plików, nawet jeśli indeks główny jest używany w kolejności według.
źródło