Używanie identyfikatora GUID jako klucza podstawowego

32

Generalnie używam identyfikatorów automatycznego przyrostu jako kluczy podstawowych w bazach danych. Próbuję poznać zalety korzystania z GUID. Przeczytałem ten artykuł: https://betterexplained.com/articles/the-quick-guide-to-guids/

Zdaję sobie sprawę, że te identyfikatory GUID są używane do identyfikacji obiektów na poziomie aplikacji. Czy są one również przechowywane jako klucz podstawowy na poziomie bazy danych. Powiedzmy, że miałem następującą klasę:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Powiedzmy, że chciałem utworzyć nową osobę w pamięci, a następnie wstawić osobę do bazy danych. Czy mogę to po prostu zrobić:

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Powiedzmy, że mam bazę danych zawierającą miliony wierszy z identyfikatorem GUID jako kluczem podstawowym. Czy to zawsze będzie wyjątkowe? Czy w ogóle rozumiem identyfikatory GUID?

Przeczytałem ten artykuł wcześniej: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Trochę mnie to myli, ponieważ wydaje się, że polecam szczęśliwe medium między identyfikatorami GUID a liczbami całkowitymi jako kluczami głównymi.

Edytuj 11/06/18

Doszedłem do wniosku, że Przewodniki są bardziej odpowiednie niż ints do moich wymagań. W dzisiejszych czasach używam CQRS, a identyfikatory GUID są ładniejsze.

Zauważam, że niektórzy programiści modelują identyfikatory GUID jako ciągi w modelu domeny, np. Tutaj: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - w tym przypadku: IdentityGuid to identyfikator GUID modelowany jako ciąg. Czy jest jakiś powód, aby to zrobić poza tym, co podano tutaj: Użyć obiektu wartości niestandardowej lub identyfikatora GUID jako identyfikatora jednostki w systemie rozproszonym? . Czy modelowanie identyfikatora GUID jako ciągu jest „normalne”, czy powinienem modelować go jako identyfikator GUID w modelu i bazie danych?

w0051977
źródło
7
Nie ma gwarancji, że będzie wyjątkowy, ale jest mało prawdopodobne, że kiedykolwiek zobaczysz kolizję. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik
2
patrz także: Kolizje UUID
komara
2
Zobacz także dba.stackexchange.com/questions/54690/... , a także wiele innych pytań - często zadawano ten temat, odpowiadano na nie i kłócono się .
Greenstone Walker
1
System, z którym obecnie pracuję, wykorzystuje UUID. Fajną właściwością jest to, że identyfikator jednoznacznie identyfikuje rekord, w przeciwieństwie do identyfikatora sekwencyjnego, który identyfikuje rekord w tej tabeli.
Justin

Odpowiedzi:

41

Identyfikatory GUID są z definicji „globalnie unikatowymi identyfikatorami”. Istnieje podobna, ale nieco inna koncepcja w Javie o nazwie UUID „Uniwersalnie unikalne identyfikatory”. Nazwy są wymienne dla wszystkich praktycznych zastosowań.

Identyfikatory GUID są kluczowe dla sposobu, w jaki Microsoft przewidział klastrowanie bazy danych do pracy, a jeśli musisz uwzględnić dane z czasami połączonych źródeł, naprawdę pomagają zapobiegać kolizjom danych.

Kilka faktów Pro-GUID:

  • Identyfikatory GUID zapobiegają kolizjom kluczy
  • Identyfikatory GUID pomagają w scalaniu danych między sieciami, maszynami itp.
  • SQL Server obsługuje półsekwencyjne GUIDY, aby zminimalizować fragmentację indeksu ( zob. Niektóre zastrzeżenia)

Niektóre brzydoty z GUID

  • Są duże, po 16 bajtów
  • Są niesprawne, więc nie można sortować według identyfikatora i mieć nadzieję na uzyskanie kolejności wstawiania, tak jak w przypadku identyfikatorów automatycznego przyrostu
  • Są trudniejsze w obsłudze, szczególnie w przypadku małych zestawów danych (takich jak tabele wyszukiwania)
  • Nowa implementacja GUID jest bardziej niezawodna na SQL Server niż w bibliotece C # (możesz mieć sekwencyjne GUID z SQL Server, w C # jest losowe)

Identyfikatory GUID zwiększą indeksy, więc koszt miejsca na dysku na indeksowanie kolumny będzie wyższy. Losowe identyfikatory GUID fragmentują twoje indeksy.

Jeśli wiesz, że nie zamierzasz synchronizować danych z różnych sieci, identyfikatory GUID mogą przenosić więcej kosztów ogólnych, niż są warte.

Jeśli musisz pobierać dane od czasami połączonych klientów, mogą one być znacznie bardziej odporne na zapobieganie kolizjom kluczy niż poleganie na ustawianiu zakresów sekwencji dla tych klientów.

Berin Loritsch
źródło
18
Rozumiem, że identyfikatory GUID są synonimami identyfikatorów UUID. UUID to standardowa nazwa. GUID jest tym, co stworzył Microsoft przed RFC 4122 .
JimmyJames,
13
„Są niesprawne, więc nie możesz sortować według ID i mam nadzieję, że dostaniesz kolejność wstawiania, tak jak możesz w przypadku identyfikatorów z auto-przyrostem” Szczerze mówiąc, nie czuję się komfortowo polegając na tym w przypadku zwykłych identyfikatorów. Chociaż w skrajnym przypadku jest możliwe, że niższy identyfikator zostanie później przypisany do dysku, wolę polegać na użytecznych danych do sortowania, takich jak datownik wstawiania. Identyfikatory należy traktować jak adresy pamięci - wszystko ma jeden, ale sama wartość jest bez znaczenia. Używaj ich co najwyżej do remisów. Zwłaszcza, że ​​jeśli masz duże obciążenie, zamówienie nie jest gwarantowane.
Clockwork-Muse
8
@CortAmmon Według Wikipedii i RFC 4122 są one synonimami. P. Leach z Microsoft był jednym z twórców RFC. Myślę, że odkąd stworzono RFC, oba są takie same. Z RFC: „UUID (uniwersalnie unikalny identyfikator), znany również jako GUID (globalnie unikalny identyfikator).” Myślę, że warto również zauważyć, że identyfikatory GUID nie zostały utworzone przez MS. Właśnie stworzyli nową nazwę dla technologii przyjętej z innych źródeł.
JimmyJames
6
„SQL Server posiada optymalizacje do obsługi identyfikatorów GUID, więc nie powinno to mieć większego wpływu na wydajność zapytań”. -1 Niezupełnie zoptymalizowany. Pracuję z DB, w którym wszystkie PK są przewodnikami, a jest to jedna z głównych przyczyn niskiej wydajności.
Andy,
7
„SQL Server posiada optymalizacje do obsługi identyfikatorów GUID, więc nie powinno to mieć większego wpływu na wydajność zapytań. ” Nieprawda. To oświadczenie zakłada, że ​​inne typy danych nie są zoptymalizowane. Serwery baz danych mają również optymalizacje do obsługi na przykład prostych wartości int. Identyfikatory GUID / UUID są znacznie wolniejsze niż użycie 4-bajtowej wartości int. 16 bajtów nigdy nie będzie tak szybkich jak 4 bajty - szczególnie na maszynie, która obsługuje maksymalnie 4 lub 8 bajtów natywnie.
Andrew Henle,
28

Czy to zawsze będzie wyjątkowe?

Zawsze? nie, nie zawsze; to skończona sekwencja bitów.

Powiedzmy, że mam bazę danych zawierającą miliony wierszy z identyfikatorem GUID jako kluczem podstawowym.

Miliony, prawdopodobnie jesteś bezpieczny. Milion milionów, a prawdopodobieństwo kolizji staje się znaczące. Są jednak dobre wieści: do tego czasu zabrakło już miejsca na dysku.

Czy mogę to po prostu zrobić?

Możesz; to nie jest całkiem dobry pomysł. Twój model domeny zwykle nie powinien generować liczb losowych; powinny stanowić dane wejściowe do twojego modelu.

Poza tym, gdy masz do czynienia z niewiarygodną siecią, w której możesz otrzymywać zduplikowane wiadomości, deterministycznie wygenerowany UUID ochroni cię przed zduplikowaniem jednostek. Ale jeśli przypiszesz każdemu nowy losowy numer, będziesz mieć więcej pracy do zidentyfikowania duplikacji.

Zobacz opis UUID oparty na nazwie w RFC 4122

Czy modelowanie identyfikatora GUID jako ciągu jest „normalne”, czy powinienem modelować go jako identyfikator GUID w modelu i bazie danych?

Myślę, że to nie ma większego znaczenia. W większości modeli domen jest to identyfikator ; jedynym pytaniem, które o to pytasz, jest to, czy jest takie samo jak jakiś inny identyfikator. Twój model domeny zwykle nie patrzy na reprezentację identyfikatora w pamięci.

Jeśli GUID jest dostępny jako „typ pierwotny” w ustawieniu agnostycznym Twojej domeny, użyłbym go; pozwala kontekstowi pomocniczemu wybrać odpowiednie optymalizacje, które mogą być dostępne.

Należy jednak pamiętać, że reprezentacja identyfikatora, zarówno w pamięci, jak i w pamięci, jest decyzją, którą podejmujesz w swojej implementacji, i dlatego powinieneś podjąć kroki w celu zapewnienia, że ​​odcisk kodu połączony z tym decyzja jest niewielka - patrz Parnas 1972 .

VoiceOfUnreason
źródło
20
+1 za „skończyło się już miejsce na dysku do tego czasu”.
w0051977,
2
Wydaje mi się, że koncepcja „ deterministycznie generowanego UUID ” jest niezbędna (patrz Skarbiec danych 2)
alk
Rzeczywiście, możliwość ponownego obliczenia UUID / GUID na podstawie innych danych jest ogromną pomocą, szczególnie w celu wykrycia duplikatów. Kiedyś zbudowałem system przetwarzania wiadomości, który przechowuje wiadomości i przepchnąłem je przez potok przetwarzania. Utworzyłem skrót wiadomości i użyłem go jako klucza podstawowego w całym systemie. po prostu to samo w sobie rozwiązało wiele problemów z identyfikacją wiadomości, kiedy musieliśmy się skalować.
Newtopian
Milion milionów = 2 ^ 40. To sprawia, że ​​2 ^ 79 par możliwych kolizji. GUID ma 2 ^ 128 bitów, więc szansa jest jedna na 2 ^ 49. O wiele bardziej prawdopodobne jest, że masz błąd, który wykorzystuje ten sam identyfikator GUID dla dwóch rekordów lub błędnie uważa, że ​​kolizja nie występuje.
gnasher729,
Wracam do moich historycznych pytań. Zanim zaakceptuję; mógłbyś spojrzeć na moją edycję?
w0051977
11

Identyfikator GUID lub UUID najprawdopodobniej będzie unikalny ze względu na sposób ich generowania i zapewnią bezpieczny sposób zagwarantowania niepowtarzalności bez konieczności komunikowania się z organem centralnym.

Korzyści z GUID jako klucza podstawowego:

  • Możesz kopiować dane między różnymi fragmentami klastra i nie musisz się martwić o kolizje PK.
  • Pozwala poznać klucz podstawowy przed wstawieniem jakichkolwiek rekordów.
  • Upraszcza logikę transakcji wstawiania rekordów potomnych.
  • Nie można łatwo zgadnąć.

W podanym przykładzie:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Określenie identyfikatora GUID przed czasem wstawienia może zaoszczędzić podróż w obie strony do bazy danych podczas wstawiania kolejnych rekordów potomnych i umożliwić ich zatwierdzenie w tej samej transakcji.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Szkodliwe dla GUID jako klucza podstawowego:

  • Mają duże 16 bajtów, co oznacza, że ​​będą one zajmować więcej miejsca w miarę dodawania indeksów i kluczy obcych.
  • Nie sortują się dobrze, ponieważ są to zasadniczo liczby losowe.
  • Użycie indeksu jest bardzo, bardzo, bardzo złe.
  • Dużo ruchomych liści.
  • Trudno je zapamiętać.
  • Trudno je werbalizować.
  • Mogą sprawić, że URL będzie trudniejszy do odczytania.

Jeśli aplikacja nie wymaga dzielenia ani klastrowania, najlepiej trzymać się mniejszych, prostszych typów danych, takich jak int lub bigint.

Wiele baz danych ma własne implementacje wewnętrzne, które próbują złagodzić problemy z pamięcią masową spowodowane przez GUID, a SQL Server ma nawet funkcję newsequentialid, która pomaga w uporządkowaniu UUID, umożliwiając lepsze wykorzystanie indeksów i ogólnie mają lepszą charakterystykę wydajności.

Ponadto z perspektywy testera, użytkownika lub programisty współpracującego z aplikacją użycie identyfikatora nad identyfikatorem GUID znacznie poprawi komunikację. Wyobraź sobie, że musisz czytać GUID przez telefon.

Ostatecznie, chyba że klastrowanie lub zaciemnianie adresów URL na dużą skalę jest wymogiem, bardziej pragmatyczne jest trzymanie się identyfikatorów z automatyczną inkrementacją.

icirellik
źródło
1
Jedną rzeczą do rozważenia jest to, że w zależności od typu UUID , zawierają informacje, które mogłyby zostać wykorzystane do identyfikacji komputera, na którym są generowane. Czysty wariant losowy może częściej zderzać się bez wystarczającej entropii. Należy to wziąć pod uwagę przed użyciem w URI.
JimmyJames
Zgoda, choć nigdy nie należy ujawniać klucza podstawowego w adresie URL. Należy zastosować bardziej odpowiednią metodę, aby zapewnić, że nie dojdzie do bezpiecznego wycieku danych do systemu zewnętrznego.s
icirellik
1
Jest jeszcze jeden przypadek użycia: ciężkie wstawianie baz danych OLTP, w których blokowanie sekwencji stanowi wąskie gardło. Według mojego przyjaciela Oracle DBA, nie jest to tak rzadkie, jak się wydaje, nie potrzebujesz do tego nawet dużej skali ani klastrów. • Na koniec należy zważyć zalety i wady (i nie mylić zalet / wad UUID z zaletami / wadami, które nie są specyficzne dla UUID, jak robią to niektórzy plakaty) i zmierzyć .
mirabilos
1
Jeśli używasz newsequentialid, musisz udać się do bazy danych, aby uzyskać identyfikator (jak z int int), prawda? Jaka jest tutaj korzyść.
w0051977,
1
@mirabilos Aby być jasnym, kiedy mówię strasznie, skończyło się na tym, że wkładki zajmowały minuty w rzędzie. Zaczęło się OK, ale po 10 tysiącach rzędów poszło bardzo szybko na boki. Jeśli nie jest to oczywiste, dziesiątki tysięcy wierszy to bardzo mały stół.
JimmyJames
4

Powiedziałbym, że nie, nie używaj identyfikatorów GUID jako kluczy podstawowych. Właściwie mam teraz do czynienia z takim DB i są one jedną z głównych przyczyn problemów z wydajnością.

Dodatkowe 12 bajtów sumuje się szybko; pamiętaj, że większość PK będzie FK w innych tabelach, a tylko trzy FK w tabeli masz teraz 48 bajtów dodatkowych na każdy wiersz. To sumuje się w tabeli i indeksach. Dodaje się także do dysku I / O. Te dodatkowe 12 bajtów należy odczytać i zapisać.

A jeśli nie używasz sekwencyjnych prowadnic, a PK są grupowane (co dzieje się domyślnie), SQL będzie od czasu do czasu musiał przenosić całe strony danych, aby ściśnąć więcej w odpowiednie „miejsce”. W przypadku bazy danych zawierającej transakcje z dużą ilością wstawek, aktualizacji i usunięć, rzeczy szybko się psują.

Jeśli potrzebujesz jakiegoś unikalnego identyfikatora do synchronizacji lub czegoś takiego, dodaj kolumnę Guid. Tylko nie rób tego PK.

Andy
źródło
4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Jest to zdecydowanie najważniejszy powód używania identyfikatorów GUID.

Ogromną zaletą jest fakt, że możesz utworzyć unikalny identyfikator bez wiedzy kodu lub komunikowania się z nim.

Możesz być pewien, że obiekt Osoby, który właśnie wygenerowałeś na swoim serwerze, telefonie PC, laptopie, urządzeniu offline lub czymkolwiek innym, jest unikalny na wszystkich twoich serwerach na całym świecie, bez względu na to, jak rozproszony.

Możesz umieścić go w dowolnym pliku bazy danych rdb lub no-sql, plik, wysłać do dowolnej usługi internetowej lub wyrzucić go natychmiast, gdy nie jest potrzebny

Nie, nigdy nie dojdzie do kolizji.

Tak, płytki mogą być nieco wolniejsze, ponieważ indeks może wymagać zmiany.

Tak, jest większy niż int.

  • edytować. musiałem zestrzelić przed ukończeniem.

Wiem, że wiele osób jest przekonanych o auto inc ints i jest to kontrowersyjny temat dotyczący DBA

Ale tak naprawdę nie mogę powiedzieć wystarczająco silnego przewodnika. Powinieneś używać prowadnic domyślnie w każdej aplikacji.

auto inc ints ma wiele wad

  • Korzystasz z rozproszonej bazy danych No-Sql. Po prostu nie możesz rozmawiać ze wszystkimi innymi instancjami, aby dowiedzieć się, jaki jest następny numer.

  • Korzystasz z systemu kolejki komunikatów. Rzeczy potrzebują identyfikatorów, zanim trafią do bazy danych

  • Tworzysz kilka elementów i edytujesz je przed zapisaniem. Każdy potrzebuje identyfikatora, zanim trafisz na db

  • Chcesz usunąć i ponownie wstawić wiersze. Upewnij się, że nie policzysz swoich automatycznych identyfikatorów i zabraknie!

  • Nie chcesz ujawniać wszystkim zamówieniom, które podjąłeś w tym roku

  • Chcesz przenosić zanonimizowane dane z produkcji w celu przetestowania i utrzymania nienaruszonych relacji. Ale nie usuwaj wszystkich istniejących danych testowych.

  • Chcesz scalić produkt z jednym najemcą w bazie danych z wieloma dzierżawcami, ale każdy ma zamówienie 56.

  • Tworzysz obiekty, które są trwałe, ale efemeryczne. (niekompletne zamówienia) ponownie, nie wykorzystuj wszystkich swoich ints z rzeczami, których już nie ma.

Lista jest nieskończona i wszystkie są prawdziwymi problemami, które przytrafiają się ludziom przez cały czas. w przeciwieństwie do wyczerpania miejsca na dysku z powodu nieco większych plików FK col

Wreszcie ogromnym problemem związanym z ints jest to, że ich zabrakło !!! ok teoretycznie nie, są ładunki. Ale w praktyce robisz tak, ponieważ ludzie nie traktują ich jak liczb losowych bez żadnego znaczenia. robią takie rzeczy

  • och, nie chcę, żeby klienci myśleli, że jesteśmy nowi. zacznij od 10.000

  • Musiałem zaimportować ładunek danych, więc po prostu zwiększyłem ziarno do 1 m, abyśmy wiedzieli, co jest importowane

  • potrzebujemy kategorii danych. każdy okres zaczyna się od następnego miliona, więc możemy użyć pierwszych cyfr jako magicznej liczby

  • Usunąłem i ponownie zaimportowałem wszystkie dane z nowymi identyfikatorami. Tak, nawet dzienniki kontroli.

  • użyj tego numeru, który jest kluczem złożonym, jako id tej drugiej rzeczy

Ewan
źródło
1
Nie ma w tym nic złego z tą odpowiedzią, ale chciałbym (aby odeprzeć dalsze głosy negatywne) być może wyraźnie zastrzegłem, że chociaż rzeczywiste aplikacje nie napotkają kolizji, jest to teoretycznie możliwe. (A może ponad 45 baz eksabajtów jest bardziej rozpowszechnionych niż myślałem ...). Chociaż uważam, że język „najważniejszy powód” jest nieco mocny, to jest to, co uważam za najbardziej przydatne.
BurnsBA
2
bardziej prawdopodobne jest, że auto inc int zderzy się z przewodnikiem
Ewan
4
-1 dla „Powinieneś używać domyślnie prowadnic w dowolnej aplikacji”. To zależy ™. I jak pokazali inni, GUID / UUID, absolutnie nie ma gwarancji, że będą unikalne.
Max Vernon
3
Odpowiedzi „Zależy” są bezużyteczne, na pewno będą jakieś dziwne aplikacje, w których int jest lepszy. Ale są szanse, że twoja aplikacja nie jest jedną z nich. Identyfikatory GUID są najbardziej unikalną rzeczą, jaką można uzyskać
Ewan
2
Myślę, że będą jakieś dziwne aplikacje, w których przewodniki są lepsze. Unikalność nie jest najważniejszą rzeczą do rozważenia. Twoje „wady” ints są ogromnie przesadzone i nie bierzesz pod uwagę żadnej z wielu wad przewodników.
Andy
2

Zdaję sobie sprawę, że te identyfikatory GUID są używane do identyfikacji obiektów na poziomie aplikacji. Czy są one również przechowywane jako klucz podstawowy na poziomie bazy danych.

Właśnie tam powinieneś przestać, dokładnie tam i przemyśleć.

Klucz podstawowy bazy danych NIGDY nie powinien mieć znaczenia biznesowego. Z definicji powinien być bez znaczenia.

Dodaj GUID jako klucz biznesowy i normalny klucz podstawowy (zwykle długi int) jako klucz podstawowy bazy danych. Zawsze możesz umieścić unikalny indeks w GUID, aby zapewnić unikalność.

Mówi się oczywiście o teorii baz danych, ale jest to również dobra praktyka. Miałem do czynienia z bazami danych, w których klucze podstawowe miały znaczenie biznesowe (jeden klient pomyślał, aby zaoszczędzić część zasobów bazy danych, wykorzystując je np. Jako numery pracowników, numery klientów itp.) I zawsze prowadzi to do problemów.

jwenting
źródło
1
Czym różni się to od odpytywania w warstwie aplikacji przy użyciu klucza podstawowego liczby całkowitej? W tym momencie służy również do identyfikacji obiektów w warstwie aplikacji. Potrzebujesz sposobu na identyfikację obiektów w bazie danych z poziomu warstwy aplikacji.
icirellik,
@icirellik klucz podstawowy jest przeznaczony do użytku wewnętrznego przez bazę danych, do łączenia rekordów nadrzędnych i podrzędnych itp. NIE jest przeznaczony do użycia przez logikę aplikacji, do tego używasz identyfikatorów biznesowych, takich jak numer produktu lub nazwa.
jwenting
2

Zawsze używaj generowanych przez bazę danych, automatycznie zwiększających klucze podstawowe (PK).

Dlaczego warto korzystać z automatycznego zwiększania wartości zamiast GUID / UUID?

  • Identyfikatory GUID (UUID) nie zapobiegają kolizjom kluczy, ponieważ nie są one unikalne i nie ma sposobu, aby uczynić je unikalnymi, ponieważ są generowane z wielu źródeł.
  • Identyfikatory GUID nie pomagają w scalaniu, ponieważ znacznie zwiększają i tak już czasochłonny proces scalania przy użyciu wyjątkowo długich, niecałkowitych kolumn PK i FK, których przetworzenie zajmuje dużo czasu. Pamiętaj, że dla większości PK będzie co najmniej 1 inny stół z co najmniej 2 kluczami tego samego rozmiaru: jego własny PK i FK z powrotem do pierwszego stołu. Wszystkie muszą zostać rozwiązane w wyniku scalenia.

Ale jak radzić sobie z odłamkami, klastrami itp.?

  • Twórz wielokolumnowe PK składające się z oddzielnych kolumn identyfikujących każdy fragment / klaster / bazę danych / cokolwiek, co zarządza własnymi kluczami automatycznego zwiększania. Na przykład...

3-kolumnowa PK dla tabeli klastrowej może być ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Ale co z...?

  • Wiele podróży do bazy danych - większość aplikacji nie musi jednoznacznie identyfikować tworzonego rekordu, dopóki nie zostanie on wstawiony do bazy danych, ponieważ ten wątek / sesja / cokolwiek działa tylko na jednym na raz. Jeśli aplikacja naprawdę potrzebuje tej możliwości, użyj wygenerowanej przez aplikację tymczasowej PK, która nie jest wysyłana do bazy danych . Niech baza danych umieści następnie swój własny PK przyrost w wierszu po wstawieniu. Wstawki będą używać tymczasowego PK, podczas gdy aktualizacje i usuwanie będą wykorzystywać stały PK przypisany przez bazę danych.

  • Wydajność - komputery mogą przetwarzać proste liczby całkowite znacznie szybciej niż cokolwiek innego ze względu na znacznie większą domenę, jeśli to możliwe, wartości na element w identyfikatorze GUID (37) w porównaniu do liczby całkowitej (10). Pamiętaj też, że każdy znak w GUID musi najpierw zostać przekonwertowany na liczbę, aby procesor mógł nim manipulować.

Częste nadużycia kluczy podstawowych PK mają tylko jeden cel ... absolutnie unikalne zidentyfikowanie wiersza w tabeli. Wszystko inne jest zbyt powszechnym niewłaściwym użyciem.

Wykrywanie brakujących danych

  • Brakujących rekordów nie można wykryć, patrząc na PK. Błogosław QA za przynajmniej próbę zapewnienia jakości danych. Jednak brak zrozumienia przez nich i programistę sposobu przypisywania kluczy we współczesnych systemach baz danych często prowadzi do błędnego przekonania, że ​​brakująca liczba w PK z automatyczną inkrementacją oznacza brak danych. Tak nie jest , ponieważ ...
  • W celu zwiększenia wydajności systemy baz danych przydzielają bloki liczb w „sekwencjach” (partie, zakresy), aby zminimalizować przejazdy do rzeczywistej bazy danych w pamięci. Rozmiar tych sekwencji liczb jest często kontrolowany przez DBA, ale może nie być dostrajany dla poszczególnych tabel.
  • Kluczem do sukcesu jest ... niewykorzystane liczby z tych sekwencji nigdy nie są zwracane do bazy danych, więc zawsze występują luki w liczbach PK.
  • Dlaczego pytasz o nieużywane liczby? Ponieważ różnorodne działania konserwacyjne bazy danych mogą powodować porzucanie sekwencji. Są to takie rzeczy, jak ponowne uruchamianie, masowe przeładowywanie tabel, niektóre rodzaje przywracania z kopii zapasowych i niektóre inne operacje.

Sortowanie

  • Sortowanie według PK jest bardzo podatne na błędy, ponieważ większość osób uważa, że ​​wyświetla ono wiersze w kolejności, w jakiej zostały utworzone, i że odpowiada to czasowi zegara. Głównie, ale niekoniecznie.
  • Silniki bazy danych są zoptymalizowane pod kątem maksymalnej wydajności, co może oznaczać opóźnienie wstawiania wyników długotrwałej skomplikowanej transakcji w celu wstawienia krótkich prostych, „poza kolejnością”, że tak powiem.
DocSalvager
źródło
Jakie są Twoje przemyślenia na temat schematu tabeli, tak że jedyną unikalną kolumną jest utworzony w bazie danych klucz automatyczny inkrementujący? W szczególności dla tabel, które nie mają klucza obcego, ale których kluczem podstawowym jest klucz obcy dla kilku powiązanych tabel?
RibaldEddie,
Dodałem znacznie więcej do tej odpowiedzi. Oryginalna odpowiedź była niepełna ze względu na wiszącą aplikację Android SE. Myślę, że główna poprawka aplikacji jest w trakcie opracowywania.
DocSalvager,
Czy twoim zdaniem byłoby w porządku, aby tabela zawierała dowolną liczbę wierszy, które były identyczne, z wyjątkiem ich automatycznego inkrementacji klucza podstawowego?
RibaldEddie,
@RibaldEddie - O ile DB ma na celu ... absolutnie. Usunięcia są łatwe. Kiedy pojawia się twój scenariusz, uważam, że to błąd, który należy naprawić w oprogramowaniu, a następnie usuwam dowolny wiersz. Znacznie bardziej powszechnym przypadkiem są jednak dwa rekordy dla tej samej rzeczy z nieco innymi danymi, więc muszą zostać scalone. Jeśli kolumna jest pusta w jednym rekordzie i ma wartość w drugim, wybór jest oczywisty i można go zautomatyzować. Często datetimestamp może być użyty do arbitrażu automatycznego scalania. Niektóre duplikaty wymagają od osoby ukończenia i zweryfikowania scalenia na podstawie reguł biznesowych.
DocSalvager,
1

Jak wszystko inne, ma to swoje zalety i wady:

Dobry:

  1. Twoje klucze są zawsze tej samej długości (bardzo duże bazy danych mogą mieć bardzo duże klucze)

  2. Wyjątkowość jest prawie gwarantowana - nawet jeśli generujesz je z oddzielnego systemu i / lub nie czytasz ostatniego identyfikatora z bazy danych

Źli:

  1. Jak wspomniano dużo powyżej - większe indeksy i magazyn danych.

  2. Nie możesz zamówić według ID, musisz złożyć zamówienie w inny sposób. Więcej indeksów, prawdopodobnie mniej wydajnych.

  3. Są mniej czytelne dla ludzi. Liczby całkowite są na ogół łatwiejsze do analizowania, zapamiętywania i pisania dla ludzi. Używanie identyfikatorów GUID jako identyfikatorów w klauzulach WHERE w wielu połączonych tabelach może spowodować stopienie głowy.

Jak wszystko, używaj ich tam, gdzie jest to właściwe, nie bądź dogmatyczny - w wielu sytuacjach auto-inkrementujące liczby całkowite są lepsze, czasami GUID są świetne.

Phil S.
źródło
0

Tak, możesz użyć GUID jako klucza podstawowego. Minusem jest rozmiar i szybkie rozdrobnienie indeksu.

O ile nie potrzebujesz wyjątkowości w bazach danych (np. Klastrze), preferowana jest liczba całkowita.

paparazzo
źródło
Generatory GUID mogą generować ten sam GUID więcej niż raz, w tym leży wada. To, czy będą, czy nie, zależy od ich ziarnistości, głównie od odstępu między tykaniami zegara. Na przykład generator oparty na zegarze może tykać tylko co 100 ms, co prowadzi do 2 identyfikatorów GUID wymaganych w ciągu 100 ms na tej maszynie, które są identyczne. W większości przypadków można tego uniknąć, ale wiele generatorów GUID działa całkowicie bez adresu IP i / lub adresu MAC i znacznika czasu.
jwenting
0

Oto moje podejście do tego problemu - rozwiązaniem jest pół drogi między wartościami GUID a int, biorąc to, co najlepsze z obu.

Klasa generuje pseudolosową (ale rosnącą z czasem) wartość Id, która jest podobna do identyfikatora GUID Comb .

Kluczową zaletą jest to, że pozwala na generowanie wartości Id na kliencie, zamiast używania wartości automatycznego przyrostu generowanych na serwerze (co wymaga podróży w obie strony) przy prawie zerowym ryzyku duplikacji wartości.

Wygenerowane wartości używają tylko 8 bajtów zamiast 16 dla GUID i nie są zależne od jednego konkretnego porządku sortowania bazy danych (np. Serwer Sql dla GUID ). Wartości można rozszerzyć, aby używały całego dalekiego zasięgu bez znaku, ale spowodowałoby to problemy z dowolną bazą danych lub innym repozytorium danych, które ma tylko podpisane typy całkowite.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
Wędrowny
źródło