Quali sono le migliori pratiche per l'utilizzo di un GUID come chiave primaria, in particolare per quanto riguarda le prestazioni?


336

Ho un'applicazione che utilizza GUID come chiave primaria in quasi tutte le tabelle e ho letto che ci sono problemi relativi alle prestazioni quando si utilizza GUID come chiave primaria. Onestamente, non ho riscontrato alcun problema, ma sto per avviare una nuova applicazione e voglio ancora utilizzare i GUID come chiavi primarie, ma stavo pensando di utilizzare una chiave primaria composita (il GUID e forse un altro campo .)

Sto usando un GUID perché sono piacevoli e facili da gestire quando si hanno ambienti diversi come database "produzione", "test" e "sviluppo" e anche per i dati di migrazione tra database.

Userò Entity Framework 4.3 e desidero assegnare il Guid nel codice dell'applicazione, prima di inserirlo nel database. (cioè non voglio lasciare che SQL generi il Guid).

Qual è la migliore pratica per la creazione di chiavi primarie basate su GUID, al fine di evitare i presunti hit delle prestazioni associati a questo approccio?


20
Il problema non è previsto. Se il tuo PK è raggruppato, quasi ogni inserto ha il potenziale per causare una divisione della pagina. Nelle versioni moderne di SQL Server questo è stato "risolto" con NEWSEQUENTIALID (), ma questo perde il vantaggio di poterlo calcolare in anticipo. Consiglio vivamente di leggere su GUID altrove in quanto questa è una domanda troppo ampia e probabilmente solleciterà una battaglia religiosa che andrà avanti per ore ...
Aaron Bertrand

4
Aggiungo anche che il word server è ambiguo in Voglio assegnare il Guid sul lato server (non voglio lasciare che SQL crei il GUID) .
Erik Philips,

Questa domanda ha somiglianze con questo "sql-server-guid-sort-algoritmo-why" stackoverflow.com/questions/7810602/…
Clinton Ward

Risposte:


495

I GUID possono sembrare una scelta naturale per la tua chiave primaria - e se davvero devi, potresti probabilmente discutere di usarla per la PRIMARY KEY della tabella. Quello che consiglio vivamente di non fare è usare la colonna GUID come chiave di clustering , che fa SQL Server per impostazione predefinita, a meno che non gli dica esplicitamente di non farlo.

Devi davvero tenere separati due problemi:

  1. la chiave primaria è un costrutto logico, una delle chiavi candidate che identifica in modo univoco e affidabile ogni riga della tabella. Questo può essere qualsiasi cosa, davvero - an INT, a GUID, a string - scegli ciò che ha più senso per il tuo scenario.

  2. la chiave di clustering (la colonna o le colonne che definiscono "l'indice di cluster" nella tabella) - questa è una cosa fisica correlata all'archiviazione e qui, un tipo di dati piccolo, stabile e in costante aumento è la scelta migliore - INTo BIGINTcome il tuo opzione predefinita.

Per impostazione predefinita, la chiave primaria su una tabella di SQL Server viene utilizzata anche come chiave di clustering, ma non è necessario che sia così! Ho visto personalmente enormi miglioramenti delle prestazioni quando ho suddiviso la precedente chiave primaria / cluster basata su GUID in due chiavi separate: la chiave primaria (logica) sul GUID e la chiave di raggruppamento (ordinamento) su una INT IDENTITY(1,1)colonna separata .

Come Kimberly Tripp - la regina dell'indicizzazione - e altri hanno affermato molte volte - a GUIDpoiché la chiave di clustering non è ottimale, poiché a causa della sua casualità, porterà a una massiccia frammentazione di pagine e indici e a prestazioni generalmente scarse.

Sì, lo so - c'è newsequentialid()in SQL Server 2005 e versioni successive - ma anche questo non è veramente e completamente sequenziale e quindi soffre anche degli stessi problemi del GUID- solo un po 'meno evidentemente.

Quindi c'è un altro problema da considerare: la chiave di clustering su una tabella verrà aggiunta a ogni singola voce su ogni indice non cluster anche sulla tabella - quindi vuoi davvero assicurarti che sia il più piccolo possibile. In genere, un INTcon oltre 2 miliardi di righe dovrebbe essere sufficiente per la stragrande maggioranza delle tabelle - e rispetto a un GUIDcome chiave di clustering, è possibile salvare centinaia di megabyte di spazio di archiviazione su disco e nella memoria del server.

Calcolo rapido - usando INTvs. GUIDcome chiave primaria e di cluster:

  • Tabella di base con 1'000'000 righe (3,8 MB contro 15,26 MB)
  • 6 indici non cluster (22,89 MB contro 91,55 MB)

TOTALE: 25 MB contro 106 MB - e questo è solo su un singolo tavolo!

Ancora un po 'di spunti di riflessione - materiale eccellente di Kimberly Tripp - leggilo, rileggilo, digeriscilo! È il vangelo dell'indicizzazione di SQL Server, davvero.

PS: ovviamente, se hai a che fare solo con poche centinaia o poche migliaia di righe, la maggior parte di questi argomenti non avrà un grande impatto su di te. Tuttavia: se entri nelle decine o centinaia di migliaia di righe o inizi a contare in milioni - allora quei punti diventano molto cruciali e molto importanti da capire.

Aggiornamento: se si desidera avere la PKGUIDcolonna come chiave primaria (ma non la chiave di clustering) e un'altra colonna MYINT( INT IDENTITY) come chiave di clustering, utilizzare questo:

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)

Fondamentalmente: devi solo dire esplicitamente il PRIMARY KEYvincolo che è NONCLUSTERED(altrimenti viene creato come indice cluster, per impostazione predefinita) - e quindi creare un secondo indice definito comeCLUSTERED

Funzionerà - ed è un'opzione valida se si dispone di un sistema esistente che deve essere "riprogettato" per le prestazioni. Per un nuovo sistema, se inizi da zero e non ti trovi in ​​uno scenario di replica, sceglierei sempre ID INT IDENTITY(1,1)la mia chiave primaria in cluster, molto più efficiente di ogni altra cosa!


2
Questa è un'ottima risposta, una cosa che vorrei menzionare è che essere in grado di generare la chiave prima di inserire è spesso utile. L'uso di "newsequentialid ()" può aiutare con il clustering, ma ciò richiede un ulteriore round-trip a SQL. Quindi un altro vantaggio dell'approccio "chiave surrogata" è che è possibile generare nuovi ID, lato client, con meno preoccupazioni sulla frammentazione dell'indice.
Andrew Theken,

2
Il modo in cui ho letto questo è che avendo sia una colonna uniqueidentifier non cluster che la colonna identità int, anche gli FK dovrebbero essere IDidentificatore univoco? Se lo fai, quando utilizzeresti direttamente la colonna Identity direttamente o no?
pinkfloydx33

2
Poca domanda, il GUID dovrebbe ora essere usato sui join o int id? Il mio istinto mi dice che dovrebbe essere usato il GUID, ma non riesco a vedere un problema tecnico usando l'int id ...
Nicolas Belley,

3
@marc_s ma in uno scenario di replica, se la colonna int è identità, non dovremmo usare il GUID poiché la colonna int può ripetersi su tutti i dispositivi?
Nicolas Belley,

6
@Kipei: il problema principale è se hai un valore così naturale - quindi sì, puoi usarlo come chiave primaria. MA : valori come DATETIMEad esempio NON sono utili per una chiave di clustering, poiché hanno solo un'accuratezza di 3,33 ms e quindi possono esistere duplicati. Quindi, in tal caso, * hai ancora bisogno di un INT IDENTITYinvece - quindi, di solito lo uso di default, dato che oltre i miei 20 anni di esperienza, una chiave naturale davvero utilizzabile non esiste quasi mai ....
marc_s

51

Uso GUID come PK dal 2005. In questo mondo di database distribuito, è assolutamente il modo migliore per unire i dati distribuiti. Puoi sparare e dimenticare l'unione delle tabelle senza la preoccupazione che gli ints si abbinino tra le tabelle unite. I join GUID possono essere copiati senza alcuna preoccupazione.

Questa è la mia configurazione per l'utilizzo dei GUID:

  1. PK = GUID. I GUID sono indicizzati in modo simile alle stringhe, quindi le tabelle con righe alte (oltre 50 milioni di record) potrebbero richiedere il partizionamento delle tabelle o altre tecniche di prestazione. SQL Server sta diventando estremamente efficiente, quindi i problemi di prestazioni sono sempre meno applicabili.

  2. PK Guid è indice NON cluster. Non clusterizzare mai un GUID a meno che non sia NewSequentialID. Ma anche in questo caso, un riavvio del server causerà gravi interruzioni nell'ordinamento.

  3. Aggiungi ClusterID Int a ogni tabella. Questo è il tuo indice CLUSTER ... che ordina il tuo tavolo.

  4. Partecipare a ClusterID (int) è più efficiente, ma lavoro con 20-30 milioni di tabelle record, quindi aderire a GUID non influisce visibilmente sulle prestazioni. Se si desidera il massimo delle prestazioni, utilizzare il concetto ClusterID come chiave primaria e unirsi a ClusterID.

Ecco la mia tabella 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)

Potresti spiegare il vincolo PK_Email? Perché hai ... Non cluster (EmailID ASC) invece di ... Non cluster (ClusterID ASC)?
Phil

2
Scommetti. Due cose principali in corso con gli indici: 1. Clustered su ClusterID: ordina la tabella su disco (frammentazione dello 0%). 2. Non raggruppato su EmailID: indicizza il campo EmailID per velocizzare le ricerche degli ID GUID. Una ricerca nel campo GUID si comporta come una stringa, quindi una ricerca EmailID sarebbe lenta senza l'indice.
Robert J. Good,

@ RobertJ.Good Ho già visto questo metodo discusso prima di aggiungere una chiave int surrogata al cluster. Ma non riesco a trovare da nessuna parte che mostri il miglioramento delle prestazioni nell'avere un indice surrogato in cluster di chiavi rispetto all'uso di un heap. Hai collegamenti a dati di riferimento?
Dale K,

1
Ciao @DaleBurrell, l'indice cluster previene la frammentazione della tabella. Il guadagno in termini di prestazioni si verifica quando la tabella cresce naturalmente in ordine su disco, con bassa frammentazione.
Robert J. Good,

@ RobertJ.Good È un'applicazione web? Cosa stai usando in urls / hrefs? guid o int?
dariol,

10

Attualmente sto sviluppando un'applicazione Web con EF Core ed ecco il modello che utilizzo:

Tutte le mie lezioni (tabelle) e un int PK e FK. Ho una colonna aggiuntiva con il tipo Guid (generato dal costruttore c #) con un indice non cluster su di esso.

Tutti i join della tabella all'interno di EF vengono gestiti tramite i tasti int, mentre tutti gli accessi dall'esterno (controller) vengono eseguiti con le guide.

Questa soluzione consente di non mostrare i tasti int sugli URL ma di mantenere il modello ordinato e veloce.


C'è qualcosa che devi fare per configurare l'intero pK come cluster, come le annotazioni dei dati, o è solo configurato automaticamente?
Allen Wang

Qual è il nome della proprietà che usi per Guid one?
Trong Phan,

3

Se usi GUID come chiave primaria e crei un indice cluster, ti suggerisco di usare il valore predefinito di NEWSEQUENTIALID ()


Perché dovresti farlo?
genuinefafa,

3

Questo link lo dice meglio di me e mi ha aiutato nel mio processo decisionale. Di solito opto per un int come chiave primaria, a meno che non abbia un'esigenza specifica di non farlo e consento anche al server SQL di generare / mantenere automaticamente questo campo a meno che non abbia un motivo specifico per non farlo. In realtà, i problemi di prestazioni devono essere determinati in base all'app specifica. Ci sono molti fattori in gioco qui tra cui, ma non solo, le dimensioni previste del db, l'indicizzazione corretta, l'interrogazione efficiente e altro ancora. Anche se le persone potrebbero non essere d'accordo, penso che in molti scenari non noterai alcuna differenza con nessuna delle due opzioni e dovresti scegliere ciò che è più appropriato per la tua app e ciò che ti consente di sviluppare più facile, più veloce e più efficace (Se non completi mai l'app che differenza fa il resto :).

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

PS Non sono sicuro del motivo per cui utilizzeresti un PK composito o quali vantaggi ritieni possano darti.


Completamente d'accordo!! Ciò significa che se ho un GUID come PK o un PK composito con GUID e un altro campo sarà lo stesso, vero?
VAAA,

1
Il PK (indice) sarebbe composto da due colonne, ma a meno che tu non abbia qualche motivo specifico per fare questo, non sembra necessario.
Matt,

1
A proposito, questa domanda è una delle domande più polarizzanti e dibattute là fuori e quindi estremamente difficile ottenere una risposta per cui ti sentirai al 100% a tuo agio. Entrambi i metodi prevedono compromessi, quindi buona fortuna :)
Matt,


0

Avere ID sequenziali rende MOLTO più facile per un hacker o un minatore di dati compromettere il tuo sito e i tuoi dati. Tienilo a mente quando scegli un PK per un sito web.


Potete fornire qualche logica o prova a sostegno di questo reclamo? Faccio fatica a vedere come un ID sequenziale potrebbe compromettere la sicurezza.
Jonaglon,

Certo, se sai che i numeri ID sono numeri interi, puoi indovinare i record sequenziali in un DB. Quindi, se esegui una query su un singolo elemento, puoi dire che l'elemento successivo è pk + 1. Se hai GUID casuali, non seguirà uno schema. Sarebbe quasi impossibile eseguire una query su record diversi da quello precedentemente interrogato (e conoscere il PK).
DaBlue,

1
Se un hacker può interrogare il tuo database sei già compromesso, non vedo come l'ID sequenziale peggiori la situazione.
Jonaglon

1
Se un utente può cambiare 1012 per un altro numero e vedere i dati che non dovrebbero, allora c'è un problema di sicurezza molto serio, quel problema non è causato dalla scelta della chiave primaria ma ne è esacerbato. Prendo il tuo punto, grazie per averlo spiegato.
Jonaglon

2
È possibile utilizzare un GUID per individuare un record nella pagina Web, che non è il PK della tabella. L'uso del parametro query in un sito Web non dovrebbe definire come strutturare lo schema DB. Il PK non ha nulla a che fare con input e parametri nell'interfaccia utente o nel sistema back-end.
Panos Roditakis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.