MySQL - długość i wydajność varchar

19

Czy deklarowanie VARCHARrozmiaru ma sens dla wydajności? Czy jest jakaś różnica (w prędkości) między VARCHAR(50)i VARCHAR(255)? Czy też definiowanie długości jest ograniczeniem logicznym / projektowym?

Sonique
źródło

Odpowiedzi:

31

Jest to bardzo częste „pytanie egzaminacyjne / rozmowa kwalifikacyjna”. Odpowiem tak dobrze, jak potrafię:

W standardowych formatach wierszy dla InnoDB i MyISAM (dynamiczny / kompaktowy) a VARCHAR(50)i a VARCHAR(255)będą przechowywać tekst łańcucha w ten sam sposób - 1 bajt dla długości i rzeczywisty łańcuch z 1 do 4 bajtów na znak (w zależności od kodowania i rzeczywisty zapisany znak).

W rzeczywistości, jeśli dobrze pamiętam, przypominam sobie kogoś, kto modyfikuje słownik danych za pomocą edytora szesnastkowego, aby zmienić coś w rodzaju VARCHAR(50)na a VARCHAR(100), więc można to zrobić dynamicznie (zwykle wymaga to rekonstrukcji tabeli). I było to możliwe, ponieważ zmiana nie wpłynęła na faktyczne dane.

Nie jest tak w przypadku VARCHAR(256), ponieważ zawsze wymagane są 2 bajty (przynajmniej) dla długości.

Tak, to oznacza, że zawsze powinniśmy zrobić VARCHAR(255), nie powinniśmy? Nie. Jest kilka powodów.

Podczas gdy InnoDB może przechowywać varchar w sposób dynamiczny, nie dotyczy to innych silników. MyISAM ma stały format rozmiaru wiersza, a tabele MEMORY mają zawsze stały rozmiar. Czy powinniśmy dbać o te inne silniki? Tak, powinniśmy, ponieważ nawet jeśli nie używamy ich bezpośrednio, tabele MEMORY są bardzo często używane do wyników pośrednich (tabele tymczasowe w pamięci) , a ponieważ wyniki nie są wcześniej znane, tabelę należy utworzyć z maksymalnym rozmiarem możliwe - VARCHAR(255)jeśli taki jest nasz typ. Jeśli możesz pomyśleć o zmarnowanym miejscu, jeśli używamy 'utf8' charsetkodowania MySQL , MEMORY zarezerwuje 2 bajty na długość + 3 * 255 bajtów na wiersz(dla wartości, które mogą zająć tylko kilka bajtów w InnoDB). To prawie 1 GB na 1 milion tabeli - tylko dla VARCHAR. Powoduje to nie tylko niepotrzebne obciążenie pamięci, ale może również powodować działania na dysku, co może spowalniać tysiące razy. Wszystko to z powodu złego wyboru określonego typu danych (niezależnie od zawartości).

Ma to również pewne konsekwencje dla InnoDB. Rozmiar indeksu jest ograniczony do 3072 bajtów i indeksów jednokolumnowych do 767 bajtów *. Jest więc bardzo prawdopodobne, że nie będziesz w stanie indeksować w pełniVARCHAR(255) pola (zakładając, że używasz utf8 lub innego kodowania o zmiennej długości).

Dodatkowo maksymalny rozmiar wiersza dla InnoDB wynosi pół strony (około 8000 bajtów), a pola o zmiennej długości, takie jak BLOB lub varchar, mogą być przechowywane poza stroną, jeśli nie mieszczą się na połowie strony . Ma to pewne konsekwencje w wydajności (czasem dobre, czasem złe, w zależności od użycia), których nie można zignorować. Spowodowało to pewną dziwność między formatami COMPACT i DYNAMIC. Zobacz na przykład: błąd 1118: zbyt duży rozmiar wiersza. utf8 innodb

Last but not least, jak przypomniała mi @ypercube, może być wymagany więcej niż 1 bajt dla długości, nawet jeśli używasz VARCHAR(255), ponieważ definicja jest w postaci znaków, podczas gdy długość przechowuje bajty. Na przykład REPEAT('ñ', 255)ma więcej niż 2 ^ 255 bajtów w utf8, więc do przechowywania jego długości potrzebowałby więcej niż 1 bajt:

mysql> SELECT LENGTH(REPEAT('ñ', 255));
+---------------------------+
| LENGTH(REPEAT('ñ', 255))  |
+---------------------------+
|                       510 |
+---------------------------+
1 row in set (0.02 sec)

mysql> SELECT CHAR_LENGTH(REPEAT('ñ', 255));
+--------------------------------+
| CHAR_LENGTH(REPEAT('ñ', 255))  |
+--------------------------------+
|                            255 |
+--------------------------------+
1 row in set (0.00 sec)

Tak więc ogólna rada polega na użyciu możliwie najmniejszego typu , ponieważ w przeciwnym razie może potencjalnie powodować problemy z wydajnością lub zarządzaniem. A VARCHAR(100)jest lepsze niż VARCHAR(255)(choć VARCHAR(20)byłoby lepsze), nawet jeśli nie znasz dokładnej długości. Staraj się być konserwatywny, ponieważ jeśli tabela nie jest zbyt duża, zawsze możesz później zmienić definicję.

Aktualizacja: Ze względu na ogromną popularność ciągów o zmiennej długości, na przykład za pomocą emoji, Oracle dąży do poprawy wydajności w tych przypadkach. W najnowszych wersjach MySQL (5.6, 5.7) InnoDB został ustawiony jako domyślny silnik zarówno wewnętrznych, jak i jawnych tabel tymczasowych, co oznacza, że ​​pola o zmiennej długości są teraz pierwszorzędnymi obywatelami. Oznacza to, że może istnieć mniej powodów, aby mieć bardzo ograniczone długości znaków (ale nadal istnieją).

(*) Druga aktualizacja : duży_prefiks_indeks jest teraz domyślnie włączony w najnowszych wersjach MySQL (8.0), ale nadal tak jest w przypadku starszych wersji lub jeśli używasz opóźnionych formatów plików / wierszy (innych niż dynamiczne lub skompresowane), ale teraz domyślnie indeksy jednokolumnowe mogą mieć maksymalnie 3072 bajty.

jynus
źródło
mała aktualizacja: MySQL-8.0.13 + domyślnie używa TempTable dla tabel tymczasowych, które mają wydajne miejsce do przechowywania dla varchars.
danblack
0

Zapomnij o włączonym 1- i 2-bajtowym prefiksie VARCHARs.

  • Ma niewielki wpływ na wydajność.
  • Jest to „2” częściej niż wynika z oczywistej reguły.

Pytanie o 255 zostało zadane i udzielono odpowiedzi wiele razy.

  • Zbyt długo VARCHARsmoże prowadzić do awarii CREATE TABLE.
  • Tabele temp mogą zamieniać się w MEMORYtabele, z VARCHARsprzekształconymi w VARCHAR. Oznacza to na przykład, że VARCHAR(255) CHARACTER SET utf8mb4chce mieć stałą długość 1020 bajtów. (To się nie powiedzie i zdegeneruje się przy użyciu MyISAM.)

Konkluzja: Nie używaj na ślepo 255 (lub 256); rób to, co ma sens dla schematu.

Rick James
źródło