Wydajność UUID w MySQL?

Rozważamy użycie wartości UUID jako kluczy podstawowych dla naszej bazy danych MySQL. Wstawiane dane są generowane z dziesiątek, setek, a nawet tysięcy zdalnych komputerów i są wstawiane z szybkością 100-40 000 wstawek na sekundę, a my nigdy nie zrobimy żadnych aktualizacji.

Sama baza danych zazwyczaj dociera do około 50 milionów rekordów, zanim zaczniemy pobierać dane, więc nie jest to ogromna baza danych, ale też nie mała. Planujemy również biegać na InnoDB, choć jesteśmy otwarci na zmiany jeśli jest lepszy silnik do tego, co robimy.

Byliśmy gotowi do pracy z uuid typu 4 Javy, ale w testach zauważyliśmy dziwne zachowanie. Po pierwsze, przechowujemy jako varchar (36) i teraz zdaję sobie sprawę, że byłoby lepiej używać binary(16) - choć o ile lepiej nie jestem pewien.

Większe pytanie brzmi: jak bardzo te losowe DANE psują indeks, gdy mamy rekordy 50M? Czy byłoby nam lepiej, gdybyśmy użyli na przykład UUID typu 1, gdzie lewe bity czas się skończył? A może powinniśmy całkowicie porzucić UUIDs i rozważyć auto_increment podstawowe klucze?

Szukam ogólnych myśli/wskazówek na temat wydajności różnych typów uuid, gdy są one przechowywane jako indeks/klucz podstawowy w MySQL. Dzięki!

Author: Patrick Lightbody, 2010-03-02

9 answers

UUID jest uniwersalnie unikalnym identyfikatorem. To jest uniwersalna część, którą powinieneś rozważyć.

Czy naprawdę potrzebujesz identyfikatorów, aby były uniwersalnie unikalne? Jeśli tak, to UUIDs może być twoim jedynym wyborem.

Zdecydowanie sugerowałbym, że jeśli używasz UID, przechowujesz je jako liczbę, a nie jako ciąg znaków. Jeśli masz rekordy 50M+, to oszczędność miejsca na dysku poprawi Twoją wydajność(chociaż nie mogłem powiedzieć o ile).

Jeśli Twoje identyfikatory nie musisz być uniwersalnie unikalny, więc nie sądzę, że możesz zrobić znacznie lepiej niż po prostu używając auto_increment, co gwarantuje, że identyfikatory będą unikalne w tabeli (ponieważ wartość będzie wzrastać za każdym razem)

 29
Author: Dancrumb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-02 17:20:11

W mojej pracy używamy UUID jako PKs. Z doświadczenia mogę ci powiedzieć, że nie używaj ich jako PKs (przy okazji SQL Server).

To jedna z tych rzeczy, że gdy masz mniej niż 1000 płyt to jest ok, ale kiedy masz miliony, to najgorsza rzecz, jaką możesz zrobić. Dlaczego? Ponieważ UUID nie są sekwencyjne, więc za każdym razem, gdy wstawiany jest nowy rekord MSSQL musi przejść do właściwej strony, aby wstawić rekord, a następnie wstawić rekord. Naprawdę brzydką konsekwencją tego jest to, że strony kończą się w różnych rozmiarach i kończą się fragmentarycznie, więc teraz musimy zrobić okresową de-fragmentację.

Kiedy używasz autoincrement, MSSQL zawsze przejdzie do ostatniej strony, a ty skończysz z równie wielkimi stronami (teoretycznie), więc wydajność wybierania tych rekordów jest znacznie lepsza (również dlatego, że wstawki nie będą blokować tabeli/strony tak długo).

Jednak dużą zaletą używania UUID jako PKs jest to, że jeśli mamy klastry DBs, nie będzie konfliktów podczas łączenia.

Polecam następujący model: 1. PK Int Identity 2. Dodatkowa kolumna generowana automatycznie jako UUID.

W ten sposób proces scalania jest możliwy(uuid byłby twoim prawdziwym kluczem, podczas gdy PK byłby tylko czymś tymczasowym, co daje dobrą wydajność).

Uwaga: najlepszym rozwiązaniem jest użycie NEWSEQUENTIALID (jak mówiłem w komentarzach), ale dla starszych aplikacji z niedużym czasem na refaktoryzację (a co gorsza, nie kontrolowanie wszystkich wstawek), to niemożliwe. Ale rzeczywiście, od 2017 roku, powiedziałbym, że najlepszym rozwiązaniem tutaj jest NEWSEQUENTIALID lub robi Guid.Grzebień z NHibernate.

Hope this helps

 62
Author: Kat Lim Ruiz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-20 21:07:00

Należy wziąć pod uwagę fakt, że Autoinkrementy są generowane pojedynczo i nie można ich rozwiązać za pomocą równoległego rozwiązania. Walka o uuid ostatecznie sprowadza się do tego, co chcesz osiągnąć, a co potencjalnie poświęcisz.

On performance, krótko :

UUID taki jak ten powyżej to 36 znaki długie, w tym myślniki. Jeśli przechowujesz ten VARCHAR (36), jesteś zmniejszy wydajność porównywania dramatycznie. To jest twój podstawowy key, nie chcesz, żeby to było powolne.

Na poziomie bitów UUID wynosi 128 bitów, co oznacza, że zmieści się w 16 bajtach, zauważ, że nie jest to zbyt czytelne dla człowieka, ale utrzyma niski poziom pamięci i jest tylko 4 razy większy niż 32-bitowy int, lub 2 razy większy niż 64-bitowy int. Użyję VARBINARY (16) Teoretycznie może to działać bez dużo kosztów.

Polecam przeczytać dwa posty:

Myślę, że pomiędzy tymi dwoma, odpowiadają na twoje pytanie.

 23
Author: Kyle Rozendo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-02 17:24:46

Staram się unikać UUID po prostu dlatego, że jest to ból do przechowywania i ból do użycia jako klucz podstawowy, ale są zalety. Głównym jest to, że są wyjątkowe.

Zazwyczaj rozwiązuję problem i unikam UUID za pomocą pól dual key.

KOLEKTOR = UNIKALNY PRZYPISANY DO MASZYNY

ID = rekord pobrany przez kolektor (pole auto_inc)

To oferuje mi dwie rzeczy. Szybkość pól auto-inc i unikalność danych przechowywanych w centralnej lokalizacji po ich zebrane i zgrupowane razem. Wiem też podczas przeglądania danych, gdzie zostały zebrane, co często jest dość ważne dla moich potrzeb.

Widziałem wiele przypadków, gdy miałem do czynienia z innymi zestawami danych dla klientów, w których zdecydowali się użyć UUID, ale nadal mają pole, gdzie dane zostały zebrane, co naprawdę jest stratą wysiłku. Wystarczy użyć dwóch (lub więcej w razie potrzeby) pól, ponieważ klucz naprawdę pomaga.

Właśnie widziałem zbyt wiele hitów wydajności przy użyciu UUID. Czują jak oszust...

 5
Author: Glenn J. Schworak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-16 17:32:38

Zamiast centralnie generować unikalne klucze dla każdego wstawiania, co powiesz na przydzielanie bloków kluczy do poszczególnych serwerów? Gdy zabraknie im kluczy, mogą poprosić o nowy blok. Następnie rozwiązujesz problem napowietrzności, podłączając każdą wkładkę.

Keyserver utrzymuje następny dostępny identyfikator

  • Serwer 1 żąda bloku id.
  • keyserver zwraca (1,1000)
    Serwer 1 może wstawić 1000 rekordów, dopóki nie będzie musiał żądać nowego bloku
  • Serwer 2 blok indeksu żądań.
  • keyserver zwraca (1001,2000)
  • itd...

Możesz wymyślić bardziej wyrafinowaną wersję, w której serwer mógłby zażądać liczby potrzebnych kluczy lub zwrócić nieużywane bloki do serwera kluczy, który oczywiście musiałby utrzymywać mapę używanych / nieużywanych bloków.

 3
Author: Bouke Versteegh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-13 15:56:28

Każdemu serwerowi przypisałbym numer ID w sposób transakcyjny. Następnie każdy wstawiony rekord po prostu automatycznie utworzy swój własny licznik. Połączenie ServerID i RecordID będzie unikalne. Pole ServerID można zindeksować i wybrać wydajność na podstawie ServerID (w razie potrzeby) może być znacznie lepiej.

 2
Author: Nikolai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-15 01:49:00

A może jakiś ręcznie wykonany UID? Daj każdemu z tysięcy serwerów ID i zrób klucz podstawowy kluczem combo autoincrement, MachineID???

 1
Author: MindStalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-02 18:08:56

Ponieważ klucz podstawowy jest generowany w sposób zdecentralizowany, nie masz możliwości użycia auto_increment.

Jeśli nie musisz ukrywać tożsamości zdalnych maszyn, użyj uuid typu 1 zamiast uuid. Są one łatwiejsze do wygenerowania i mogą przynajmniej nie zaszkodzić wydajności bazy danych.

To samo tyczy się varchar (char, naprawdę) vs. binary: może pomóc tylko w sprawach. Czy to naprawdę ważne, jak bardzo wydajność jest lepsza?

 1
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-03 00:23:16

Krótka odpowiedź jest taka, że wiele baz danych ma problemy z wydajnością (w szczególności z dużymi wolumenami wstawiania) z powodu konfliktu między ich metodą indeksowania a celową entropią uuid w bitach wysokiego rzędu. Istnieje kilka popularnych hacków:

  • Wybierz inny typ indeksu (np. nonclustered na MSSQL), który nie przeszkadza
  • munge danych, aby przenieść entropię do bitów niższego rzędu (np. zmiana kolejności bajtów uuid V1 W MySQL)
  • uczyń UUID kluczem wtórnym z auto-increment int primary key

... ale to są wszystkie hacki ... i pewnie te delikatne.

Najlepszą odpowiedzią, ale niestety najwolniejszą, jest żądanie, aby twój dostawca ulepszył swój produkt, aby mógł poradzić sobie z uuid jako kluczami podstawowymi, tak jak każdy inny typ. Nie powinni zmuszać cię do robienia własnych na wpół upieczonych hack, aby nadrobić ich niepowodzenie w rozwiązaniu tego, co stało się powszechnym przypadkiem użycia i będzie nadal rosnąć.

 0
Author: Stephen Sprunk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 21:47:47