Jaki jest wpływ na wydajność używania CHAR vs VARCHAR na polu o stałym rozmiarze?

58

Mam indeksowaną kolumnę, która przechowuje skrót MD5. Dlatego kolumna zawsze będzie przechowywać wartość 32 znaków. Z jakiegokolwiek powodu został on stworzony raczej jako varchar niż char. Czy warto zmigrować bazę danych, aby przekonwertować ją na znak? To jest w MySQL 5.0 z InnoDB.

Jason Baker
źródło
6
OSTRZEŻENIE To pytanie i odpowiedzi zostały napisane przed InnoDB i utf8 były wartościami domyślnymi.
Rick James

Odpowiedzi:

56

Podobne pytanie zostało zadane wcześniej

Wpływ na wydajność rozmiarów VARCHAR MySQL

Oto fragment mojej odpowiedzi

Musisz zdać sobie sprawę z kompromisów używania CHAR vs VARCHAR

W przypadku pól CHAR alokujesz dokładnie to, co dostajesz. Na przykład CHAR (15) przydziela i przechowuje 15 bajtów, bez względu na to, jak postacie umieszczasz w polu. Manipulowanie ciągami znaków jest proste i jednoznaczne, ponieważ wielkość pola danych jest całkowicie przewidywalna.

Dzięki polom VARCHAR otrzymujesz zupełnie inną historię. Na przykład VARCHAR (15) faktycznie dynamicznie przydziela do 16 bajtów, do 15 na dane i co najmniej 1 dodatkowy bajt do przechowywania długości danych. Jeśli masz ciąg „hello” do zapisania, który zajmie 6 bajtów, a nie 5. Manipulowanie ciągiem zawsze musi przeprowadzać sprawdzanie długości we wszystkich przypadkach.

Kompromis jest bardziej widoczny, gdy wykonujesz dwie rzeczy: 1. Przechowywanie milionów lub miliardów wierszy 2. Indeksowanie kolumn, które są albo CHAR albo VARCHAR

TRADEOFF # 1 Oczywiście VARCHAR ma tę zaletę, że dane o zmiennej długości wygenerowałyby mniejsze wiersze, a tym samym mniejsze pliki fizyczne.

TRADEOFF # 2 Ponieważ pola CHAR wymagają mniejszej manipulacji ciągiem ze względu na ustaloną szerokość pola, wyszukiwanie indeksów względem pola CHAR jest średnio o 20% szybsze niż w przypadku pól VARCHAR. To nie jest żadna hipoteza z mojej strony. Książka MySQL Database Design and Tuning wykonała coś cudownego na stole MyISAM, aby to udowodnić. Przykład w książce zrobił coś takiego:

ALTER TABLE tblname ROW_FORMAT=FIXED;

Ta dyrektywa zmusza wszystkie VARCHAR do zachowywania się jak CHAR. Zrobiłem to podczas mojej poprzedniej pracy w 2007 roku i wziąłem tabelę 300 GB i przyspieszyłem wyszukiwanie indeksów o 20%, nie zmieniając niczego innego. Działa jak opublikowano. Jednak stworzył stół prawie dwukrotnie większy, ale to po prostu wraca do kompromisu nr 1.

Możesz przeanalizować przechowywane dane, aby zobaczyć, co MySQL zaleca do definicji kolumny. Po prostu uruchom następujące polecenie dla dowolnej tabeli:

SELECT * FROM tblname PROCEDURE ANALYSE();

Spowoduje to przejście całej tabeli i zalecenie definicji kolumn dla każdej kolumny na podstawie zawartych w niej danych, minimalnych wartości pól, maksymalnych wartości pól i tak dalej. Czasami musisz po prostu zachować zdrowy rozsądek przy planowaniu CHAR vs VARCHAR. Oto dobry przykład:

Jeśli przechowujesz adresy IP, maska ​​takiej kolumny ma maksymalnie 15 znaków (xxx.xxx.xxx.xxx). Skoczyłbym od razu CHAR(15)w mgnieniu oka, ponieważ długości adresów IP nie będą się tak bardzo różnić, a dodatkowa złożoność operacji na łańcuchach kontrolowana przez dodatkowy bajt. Nadal możesz zrobić PROCEDURE ANALYSE()przeciwko takiej kolumnie. Może nawet polecić VARCHAR. W tym przypadku moje pieniądze byłyby nadal na CHAR zamiast VARCHAR.

Problemy z CHAR vs VARCHAR można rozwiązać tylko poprzez odpowiednie planowanie. Z wielką mocą wiąże się wielka odpowiedzialność (banał, ale prawda).

AKTUALIZACJA

Jeśli chodzi o MD5, obliczenia strlenwewnętrzne powinny zostać wyeliminowane podczas przełączania całego formatu wiersza. Nie byłoby potrzeby zmiany definicji pola.

Jeśli klucz MD5 jest jedynym VARCHAR, wybrałbym go i przekonwertowałem format wiersza tabeli na naprawiony . Jeśli obecna jest znacząca liczba innych pól VARCHAR, skorzystaliby również. W zamian stół powiększyłby się do około dwukrotności swojego rozmiaru. Ale zapytania powinny przyspieszyć o około 20% więcej bez dodatkowego strojenia.

RolandoMySQLDBA
źródło
1
Myślę, że użyłbym char (4) lub czegoś w rodzaju liczby całkowitej bez znaku dla adresu IP
Jack Douglas
@JackPDouglas Masz rację co do tego punktu.
RolandoMySQLDBA
W każdym razie, czy indeksy nie są przechowywane ze stałą długością? Nie rozumiem, w jaki sposób zmiana formatu pamięci na poprawione wyszukiwanie indeksów o stałej długości. Czy masz na myśli poprawę skanowania tabel?
Marcus Adams,
1
@JackDouglas, dlaczego nie biti binary?
Pacerier,
@Pacerier, że byłoby lepiej, zgadzam się :)
Jack Douglas,
19

Wygląda na to, że zaoszczędzisz 1 bajt na wartość lub około 3%, konwertując na a char. Prawdopodobnie nie warto, jeśli i tak przechowujesz MD5 na hexie - binaryzamiast tego możesz zaoszczędzić 50% .

Dzięki Ovais (patrz komentarze) za zwrócenie uwagi, że char(32)może używać dużo więcej niż 32 bajtów, jeśli używasz zestawu znaków wielobajtowych.

Podziękowania dla Ricka Jamesa za wskazanie, że należy użyć unhexfunkcji do konwersji ciągu szesnastkowego na binarny:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| długość (bar) |
| ----------: |
| 32 |
| 16 |

db <> skrzypce tutaj

Jack Douglas
źródło
Dobry telefon na przejście na binarny.
RThomas
Planuję przekonwertować to na plik binarny. Teraz, gdy o tym myślę, rozmiar nie powinien być inny, zależnie od tego, czy używam bajtu czy znaku, ponieważ nasze kodowanie to utf-8. A może się mylę?
Jason Baker
@Jason - kodowanie nie dotyczy binary- czy też źle zrozumiałem?
Jack Douglas
3
dla kolumny char (32) z zestawem znaków utf-8 każda wartość wymagałaby 32 x 3 bajtów do przechowywania. Dlaczego warto ustawić wartość skrótu MD5 na utf-8. Konwersja na binarną (32) wymagałaby 32 bajtów na wartość.
ovais.tariq
1
Zmiana na BINARYniewiele robi, chyba że używasz również UNHEX(). Oznacza to, że można przechowywać UNHEX(MD5(x))do 16-bajtowy BINARY(16)zaoszczędzić znaczną przestrzeń na przechowywanie MD5(x)do CHAR(32) CHARACTER SET ascii.
Rick James
15

Moim zdaniem nie warto się zmieniać. Jeśli przejrzysz tutaj dokumentację, powinna ona zilustrować różnicę między nimi. W twoim scenariuszu użytkowania jeden nie oferuje żadnych znaczących korzyści w stosunku do drugiego, chyba że naprawdę martwisz się dodatkowym narzutem związanym z rozmiarem wiersza.

http://dev.mysql.com/doc/refman/5.0/en/char.html

Zwróć też uwagę na pierwszy komentarz do dokumentacji, do której linkuję powyżej ... „CHAR przyspieszy twój dostęp tylko wtedy, gdy cały rekord ma ustalony rozmiar. To znaczy, jeśli użyjesz dowolnego obiektu o zmiennej wielkości, równie dobrze możesz zrobić je wszystkie zmienna wielkość. Nie zyskujesz prędkości, używając CHAR w tabeli, która zawiera również VARCHAR ”

RThomas
źródło
To „przyspieszenie” dotyczy MyISAM, a nie InnoDB.
Rick James