Jakie są najlepsze praktyki dotyczące używania identyfikatora GUID jako klucza podstawowego, szczególnie w zakresie wydajności?

336

Mam aplikację, która używa GUID jako klucza podstawowego w prawie wszystkich tabelach i przeczytałem, że istnieją problemy z wydajnością podczas używania GUID jako klucza podstawowego. Szczerze mówiąc, nie widziałem żadnego problemu, ale mam zamiar uruchomić nową aplikację i nadal chcę używać identyfikatorów GUID jako kluczy podstawowych, ale myślałem o użyciu kompozytowego klucza podstawowego (GUID i może innego pola .)

Korzystam z GUID, ponieważ są one ładne i łatwe w zarządzaniu, gdy masz różne środowiska, takie jak bazy danych „produkcyjne”, „testowe” i „programistyczne”, a także do migracji danych między bazami danych.

Będę korzystał z Entity Framework 4.3 i chcę przypisać Guid do kodu aplikacji, przed wstawieniem go do bazy danych. (tzn. nie chcę, aby SQL generował Guid).

Jaka jest najlepsza praktyka tworzenia kluczy podstawowych opartych na GUID, aby uniknąć przypuszczalnych spadków wydajności związanych z tym podejściem?

VAAA
źródło
20
Problem nie jest przypuszczalny. Jeśli twoja PK jest skupiona, prawie każda wstawka może spowodować podział strony. W nowoczesnych wersjach SQL Server zostało to „naprawione” za pomocą NEWSEQUENTIALID (), ale traci to możliwość wcześniejszego obliczenia. Zdecydowanie polecam przeczytanie GUID w innym miejscu, ponieważ jest to zbyt szerokie pytanie i prawdopodobnie zainicjuje religijną bitwę, która toczy się godzinami ...
Aaron Bertrand
4
Dodam również, że serwer słów jest niejednoznaczny. Chcę przypisać Guid po stronie serwera (nie chcę, aby SQL tworzył GUID) .
Erik Philips,
To pytanie ma podobieństwa do tego „sql-server-guid-sort-algorytm-dlaczego” stackoverflow.com/questions/7810602/…
Clinton Ward

Odpowiedzi:

495

Identyfikatory GUID mogą wydawać się naturalnym wyborem dla twojego klucza podstawowego - a jeśli naprawdę musisz, prawdopodobnie możesz argumentować, aby użyć go dla KLUCZA PODSTAWOWEGO tabeli. Zdecydowanie odradzam korzystanie z kolumny GUID jako klucza klastrowania , co SQL Server domyślnie robi, chyba że wyraźnie powiesz, żeby tego nie robił .

Naprawdę musisz rozdzielić dwa problemy:

  1. klucz podstawowy jest logiczną konstrukcją - jeden z kluczy kandydujących, który jednoznacznie identyfikuje i niezawodnie każdego wiersza w tabeli. To może być cokolwiek, naprawdę - W INT, A GUID, łańcuch - wybrać to, co sprawia, że największy sens dla scenariusza.

  2. klucz klastrów (kolumna lub kolumn, które definiują „klastrowego indeksu” na stole) - jest to fizyczne rzeczy związane z magazynowaniem, a tu, mały, stabilny, coraz większa typ danych jest najlepszym pick - INTalbo BIGINTjako swój Domyślna opcja.

Domyślnie klucz podstawowy w tabeli programu SQL Server jest również używany jako klucz klastrowania - ale nie musi tak być! Osobiście widziałem ogromny wzrost wydajności, gdy dzielę poprzedni klucz podstawowy / klastrowany oparty na GUID na dwa osobne klucze - klucz podstawowy (logiczny) na GUID i klucz grupowania (porządkowania) w osobnej INT IDENTITY(1,1)kolumnie.

Jak Kimberly Tripp - Królowa Indeksowania - i inni wspominali wiele razy - GUIDklucz klastrowania nie jest optymalny, ponieważ ze względu na jego losowość doprowadzi do ogromnej fragmentacji stron i indeksów oraz ogólnie złej wydajności.

Tak, wiem - jest newsequentialid()w SQL Server 2005 i nowszych - ale nawet to nie jest w pełni i całkowicie sekwencyjne, a zatem cierpi z powodu tych samych problemów co GUID- tylko trochę mniej wyraźnie.

Jest jeszcze jeden problem do rozważenia: klucz klastrowania w tabeli zostanie dodany do każdego wpisu w każdym indeksie nieklastrowanym w tabeli - dlatego naprawdę chcesz mieć pewność, że jest tak mały, jak to możliwe. Zazwyczaj INTponad 2 miliardy wierszy powinno wystarczyć do przeważającej większości tabel - w porównaniu z GUIDkluczem klastrowym możesz zaoszczędzić sobie setki megabajtów pamięci na dysku i w pamięci serwera.

Szybkie obliczenia - użycie INTvs. GUIDjako klucza podstawowego i klucza grupowania:

  • Tabela bazowa z 1 000 000 wierszy (3,8 MB vs. 15,26 MB)
  • 6 indeksów nieklastrowych (22,89 MB vs. 91,55 MB)

RAZEM: 25 MB vs. 106 MB - i to tylko na jednym stole!

Więcej jedzenia do przemyślenia - doskonałe rzeczy Kimberly Tripp - przeczytaj, przeczytaj jeszcze raz, przetrawiaj! To naprawdę ewangelia indeksowania SQL Server.

PS: oczywiście, jeśli masz do czynienia z zaledwie kilkuset lub kilkoma tysiącami wierszy - większość z tych argumentów tak naprawdę nie będzie miała na ciebie większego wpływu. Jednak: jeśli wejdziesz w dziesiątki lub setki tysięcy wierszy lub zaczniesz liczyć w milionach - wtedy punkty te staną się bardzo ważne i bardzo ważne do zrozumienia.

Aktualizacja: jeśli chcesz mieć PKGUIDkolumnę jako klucz podstawowy (ale nie klucz klastrowania), a kolejną kolumnę MYINT( INT IDENTITY) jako klucz klastrowania - użyj tego:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Zasadniczo: musisz tylko wyraźnie powiedzieć PRIMARY KEY, że jest to ograniczenie NONCLUSTERED(w przeciwnym razie domyślnie jest on tworzony jako indeks klastrowany) - a następnie tworzysz drugi indeks, który jest zdefiniowany jakoCLUSTERED

To zadziała - i jest to poprawna opcja, jeśli masz istniejący system, który musi zostać „przeprojektowany” pod kątem wydajności. W przypadku nowego systemu, jeśli zaczynasz od zera i nie jesteś w scenariuszu replikacji, to zawsze wybrałbym ID INT IDENTITY(1,1)jako mój klastrowany klucz podstawowy - znacznie bardziej wydajny niż cokolwiek innego!

marc_s
źródło
2
To świetna odpowiedź, jedną rzeczą, o której wspomnę, jest to, że możliwość wygenerowania klucza przed wstawieniem jest często przydatna. Użycie „newsequentialid ()” może pomóc w klastrowaniu, ale wymaga to dodatkowej podróży w obie strony do SQL. Kolejną zaletą podejścia „klucza zastępczego” jest to, że można generować nowe identyfikatory, po stronie klienta, z mniejszymi obawami dotyczącymi fragmentacji indeksu.
Andrew Theken
2
Sposób, w jaki to czytam, jest taki, że mając zarówno nieklastrowaną kolumnę unikatowego identyfikatora, jak i kolumnę tożsamości int, FK również powinny być unikatowym identyfikatorem? Jeśli to zrobisz, kiedy faktycznie użyjesz kolumny tożsamości bezpośrednio, czy nie?
pinkfloydx33
2
Małe pytanie, czy GUID powinien być teraz używany przy sprzężeniach, czy identyfikator wewnętrzny? Instynkt podpowiada mi, że należy użyć GUID, ale nie widzę problemu technicznego przy użyciu int id ...
Nicolas Belley
3
@marc_s, ale w scenariuszu replikacji, jeśli kolumna int jest tożsamością, czy nie powinniśmy używać identyfikatora GUID, ponieważ kolumna int może się powtarzać na różnych urządzeniach?
Nicolas Belley,
6
@Kipei: główne problemy to JEŚLI masz taką naturalną wartość - wtedy tak, możesz użyć jej jako klucza podstawowego. ALE : wartości takie jak DATETIMEna przykład NIE są przydatne dla klucza klastrowania, ponieważ mają one jedynie dokładność 3,33 ms, a zatem mogą istnieć duplikaty. Więc w takim przypadku * nadal potrzebujesz INT IDENTITYzamiast tego - dlatego zwykle używam tego domyślnie, ponieważ z mojego ponad 20-letniego doświadczenia naprawdę przydatny naturalny klucz prawie nigdy nie istnieje naprawdę ...
marc_s
51

Używam GUID jako PK od 2005 roku. W tym świecie rozproszonych baz danych jest to absolutnie najlepszy sposób łączenia rozproszonych danych. Możesz uruchamiać i zapominać o scalaniu tabel bez martwienia się o dopasowanie ints do połączonych tabel. Połączenia GUID można bez obaw kopiować.

Oto moja konfiguracja używania identyfikatorów GUID:

  1. PK = GUID. Identyfikatory GUID są indeksowane podobnie jak ciągi, więc tabele wysokich wierszy (ponad 50 milionów rekordów) mogą wymagać partycjonowania tabel lub innych technik wydajności. SQL Server staje się niezwykle wydajny, więc problemy z wydajnością mają coraz mniejsze zastosowanie.

  2. PK Guid jest indeksem nieklastrowanym. Nigdy nie klastruj indeksu GUID, chyba że jest to NewSequentialID. Ale nawet wtedy restart serwera spowoduje poważne przerwy w składaniu zamówień.

  3. Dodaj ClusterID Int do każdej tabeli. To jest twój CLUSTERED Index ... który porządkuje twój stół.

  4. Dołączanie do ClusterIDs (int) jest bardziej wydajne, ale pracuję z 20-30 milionami tabel rekordów, więc dołączanie GUID nie ma widocznego wpływu na wydajność. Jeśli chcesz uzyskać maksymalną wydajność, użyj koncepcji ClusterID jako klucza podstawowego i dołącz do ClusterID.

Oto moja tabela e-mail ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Good
źródło
Czy możesz wyjaśnić ograniczenie PK_Email? Dlaczego masz ... NonClustered (EmailID ASC) zamiast ... Nonclustered (ClusterID ASC)?
Phil
2
Ty stawiasz Dwie główne rzeczy związane z indeksami: 1. Clustered na ClusterID - porządkuje tabelę na dysku (fragmentacja 0%). 2. NonClustered on EmailID - Indeksuje pole EmailID, aby przyspieszyć wyszukiwanie identyfikatorów GUID. Wyszukiwanie w polu GUID zachowuje ciąg znaków, więc wyszukiwanie bez identyfikatora byłoby powolne.
Robert J. Good,
@ RobertJ.Good Widziałem już tę metodę omawianą, tj. Dodawanie zastępczego klucza int do klastra. Ale nie mogę znaleźć nigdzie, co pokazuje wzrost wydajności dzięki indeksowi klastrowanemu kluczowi zastępczemu przy użyciu sterty. Czy masz jakieś linki do danych porównawczych?
Dale K
1
Cześć @DaleBurrell, indeks klastrowy ma zapobiegać fragmentacji tabeli. Wzrost wydajności ma miejsce, gdy tabela naturalnie rośnie w kolejności na dysku, z małą fragmentacją.
Robert J. Good
@ RobertJ.Good Czy to aplikacja internetowa? Czego używasz w urlach / hrefach? GUID czy INT?
dariol,
10

Obecnie tworzę aplikację internetową z EF Core i oto wzór, którego używam:

Wszystkie moje zajęcia (tabele) oraz int PK i FK. Mam dodatkową kolumnę z typem Guid (generowanym przez konstruktor c #) z indeksem nieklastrowanym.

Wszystkimi połączeniami tabeli w EF zarządza się za pomocą kluczy int, podczas gdy cały dostęp z zewnątrz (kontrolery) odbywa się za pomocą prowadnic.

To rozwiązanie pozwala nie wyświetlać kluczy int na adresach URL, ale pozwala zachować porządek i szybkość modelu.

EricImhauser
źródło
Czy jest coś, co trzeba zrobić, aby skonfigurować liczbę całkowitą pK jako klastrowaną, na przykład adnotacje danych, czy też jest ona konfigurowana automatycznie?
Allen Wang
Jakiej nazwy nieruchomości używasz dla Guid?
Trong Phan
3

Jeśli używasz GUID jako klucza podstawowego i tworzysz klastrowany indeks, sugeruję użyć dla niego domyślnej wartości NEWSEQUENTIALID ()

AnandPhadke
źródło
dlaczego chcesz to zrobić?
oryginalfafa
3

Ten link mówi to lepiej niż mogłem i pomógł mi w podejmowaniu decyzji. Zwykle wybieram int jako klucz podstawowy, chyba że mam określoną potrzebę, a także pozwalam serwerowi SQL na automatyczne generowanie / obsługę tego pola, chyba że mam konkretny powód, aby tego nie robić. W rzeczywistości problemy z wydajnością należy ustalić na podstawie konkretnej aplikacji. W grę wchodzi wiele czynników, w tym między innymi oczekiwany rozmiar bazy danych, prawidłowe indeksowanie, wydajne zapytania i inne. Chociaż ludzie mogą się nie zgadzać, myślę, że w wielu scenariuszach nie zauważysz różnicy w żadnej z tych opcji i powinieneś wybrać to, co jest bardziej odpowiednie dla Twojej aplikacji, a co pozwala na łatwiejsze, szybsze i bardziej efektywne tworzenie (Jeśli nigdy nie ukończysz aplikacji jaką różnicę robi reszta :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS Nie jestem pewien, dlaczego miałbyś skorzystać z Composite PK ani jakie korzyści, które według ciebie by to dały.

Matt
źródło
Kompletnie się zgadzam!! Ale to oznacza, że ​​jeśli mam GUID jako PK lub Composite PK z GUID i inne pole będzie takie samo, prawda?
VAAA,
1
PK (indeks) składałby się z dwóch kolumn, ale chyba że masz jakiś konkretny powód biznesowy, wydaje się to niepotrzebne.
Matt
1
BTW to pytanie jest jednym z najbardziej spolaryzowanych i dyskutowanych pytań, dlatego niezwykle trudno jest uzyskać odpowiedź, na którą poczujesz się w 100% swobodnie. Obie metody wiążą się z kompromisami, więc powodzenia :)
Mat.
0

Posiadanie identyfikatora sekwencyjnego znacznie ułatwia hakerowi lub eksploratorowi danych złamanie zabezpieczeń witryny i danych. Należy o tym pamiętać przy wyborze PK dla strony internetowej.

DaBlue
źródło
Czy możesz podać logikę lub dowód na poparcie tego roszczenia? Próbuję zobaczyć, w jaki sposób sekwencyjny identyfikator może zagrozić bezpieczeństwu.
jonaglon
Oczywiście, jeśli wiesz, że numery ID są liczbami całkowitymi, możesz zgadywać sekwencyjnie zapisy w DB. Więc jeśli zapytasz o pojedynczy element, możesz powiedzieć, że następny element to pk + 1. Jeśli masz losowe GUIDY, nie będzie ono zgodne ze wzorem. Byłoby prawie niemożliwe zapytanie o inne rekordy niż ten, który wcześniej sprawdziłeś (i znasz PK).
DaBlue
1
Jeśli haker może wysłać zapytanie do bazy danych, której już jesteś zagrożony, nie widzę, w jaki sposób sekwencyjny identyfikator może pogorszyć sytuację.
jonaglon
1
Jeśli użytkownik może zmienić numer 1012 na inny numer i zobaczyć dane, których nie powinien, to istnieje bardzo poważny problem bezpieczeństwa, problem ten nie jest spowodowany wyborem klucza podstawowego, ale go pogarsza. Rozumiem, o co ci chodzi, dziękuję, że to przeliterowałeś.
jonaglon
2
Możesz użyć identyfikatora GUID do zlokalizowania rekordu na stronie internetowej, który nie jest PK tabeli. Użycie parametru zapytania w witrynie internetowej nie powinno definiować sposobu strukturyzowania schematu DB. PK nie ma nic wspólnego z danymi wejściowymi i parametrami w interfejsie użytkownika lub systemie zaplecza.
Panos Roditakis