Indeksowanie MySQL VarChar

10

Próbuję zindeksować blogentriesbazę 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_iddo SELECTzapytania, 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 VarChari należy to zmienić na coś innego?

Usiłuję, aby wszystkie moje zapytania korzystały z indeksu, ponieważ uruchamiam wysokie wartości Handler_read_rndi Handler_read_rnd_nextwartości.

Jeśli potrzebujesz innych informacji, też mogę je opublikować.


źródło
fileort oznacza, że ​​wykonuje sortowanie na dysku.
Kermit,
Spróbuj dodać WHERE 1=1do drugiego zapytania.
Kermit,
Która to wersja MySQL? Jaki jest twój rozmiar bufora sortowania ( SELECT @@sort_buffer_size)?
ort @ njk jest wynikiem części zapytania „ORDER BY”
1
@TashPemhiwa Niekoniecznie zobacz pierwsze zdanie.
Kermit

Odpowiedzi:

6

Ponieważ nie masz WHEREklauzuli 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.

Joe Stefanelli
źródło
Z pewnością MySQL powinien używać indeksu dla ORDER BY?
eggyal
@eggyal Nie, jeśli jest za duży na pamięć.
Kermit,
@ njk: To nie ma sensu ... może przeglądać indeks w kolejności, bez konieczności ładowania całej pamięci do pamięci. Wyniki zostaną posortowane bez konieczności wykonywania sortowania plików.
eggyal
@eggyal Chciałbym zakwestionować rozmiar varchar(5000).
Kermit,
@njk: Ale ta kolumna nie jest ani w indeksie, ani nie jest używana w sortowaniu.
eggyal
2

Jak udokumentowano w części ORDER BYOptymalizacja :

W przypadku wolnych zapytań, dla których filesortnie jest używany, spróbuj obniżyć max_length_for_sort_datado wartości odpowiedniej do wyzwolenia a filesort.

W swoim artykule na blogu Czym dokładnie jest read_rnd_buffer_size , Peter Zaitsev wyjaśnia:

Dla mnie oznacza to, że od MySQL 4.1 ta opcja jest używana w wąskim zakresie przypadków - jeśli pobierzesz kilka pól (mniej niż max_length_for_sort_data ) dane powinny być przechowywane w buforze sortowania i pliku sortowania, więc nie byłoby potrzeby read_rnd_buffer, jeśli wybrane kolumny są długie, więc są dłuższe niż max_length_for_sort_data , co często oznacza, że ​​są wśród nich kolumny TEXT / BLOB. Zostałby jednak użyty, jeśli jest duża liczba kolumn lub używane są długie kolumny VARCHAR - potrzeba tylko kilku UTF8 VARCHAR (255), aby utworzyć wiersz dłuższy niż max_length_for_sort_data w swojej prezentacji statycznej.

Sugeruje to, że max_length_for_sort_datajest to limit całkowitego rozmiaru wybieranych kolumn, powyżej którego filesortzostanie 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 dlatego filesortjest używane. Aby podnieść limit do 8 KB, możesz:

SET SESSION max_length_for_sort_data = 8192;
Eggyal
źródło
Mam tabelę z bardzo podobną konfiguracją do tej, a to ustawienie nie wydaje się powodować żadnych zmian w korzystaniu z sortowania plików.
@muffinista: To interesujące. Podejrzewam, że może to być związane z niektórymi innymi ustawieniami bufora na odpowiedź @ RolandoMySQLDBA ?
eggyal
2

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:

Społeczność
źródło
Pierwsze zapytanie OP „ zawiera dane o zmiennej długości w MySQL i nie ma indeksu, który pasowałby do WSZYSTKICH żądanych kolumn ”, ale filesortnajwyraź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).
eggyal
Nie mam czasu na testowanie, ale zastanawiam się, czy jest to uruchamiane przez posiadanie zmiennej VARCHAR, która wymaga 2 bajtów do przechowywania długości, jak określono w dev.mysql.com/doc/refman/5.1/en/char. html - więc pierwsze zapytanie mieści się w tym limicie, ale drugie nie.
0

Spróbuj dodać WHEREklauzulę do swoich zapytań.

Z indeksu można korzystać, nawet jeśli ORDER BY nie pasuje dokładnie do indeksu, o ile wszystkie nieużywane części indeksu i wszystkie dodatkowe kolumny ORDER BY są stałymi w klauzuli WHERE . W niektórych przypadkach MySQL nie może użyć indeksów do rozwiązania ORDER BY , chociaż nadal używa indeksów do znalezienia wierszy pasujących do klauzuli WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


źródło
Ale w tym przypadku ORDER BY nie pasuje indeks dokładnie, więc nie ma potrzeby, aby mieć WHEREklauzulę.
eggyal
W zdarzeniu w witrynie mam klauzulę „gdzie”, więc wiem, że nie jest to przyczyną sortowania plików. Zastanawiam się, czy to użycie Varchara?
0

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
Jak udokumentowano na podstawie The CHARi VARCHARtypó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ść a VARCHARw 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.
eggyal
0

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.

mvp
źródło
Wygląda na to, że mylisz wiele rzeczy z Jak uniknąć pełnego skanowania tabeli , które dotyczą używania indeksu w spełniających JOINwarunki i WHEREklauzulach, a nie ORDER BYklauzulach.
eggyal
Dokładnie odwrotnie. W tym szczególnym przypadku pełny skan tabeli jest DOBRY, ponieważ jest SZYBCIEJ niż czytanie według kolejności indeksów.
0

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