Utilizzo di un GUID come chiave primaria


32

In genere utilizzo gli ID di incremento automatico come chiavi primarie nei database. Sto cercando di imparare i vantaggi dell'utilizzo dei GUID. Ho letto questo articolo: https://betterexplained.com/articles/the-quick-guide-to-guids/

Mi rendo conto che questi GUID vengono utilizzati per identificare oggetti a livello di applicazione. Sono anche memorizzati come chiave primaria a livello di database. Ad esempio, supponiamo che avessi la seguente classe:

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

//Person Methods follow
}

Supponiamo che volessi creare una nuova persona in memoria e quindi inserire la persona in un database. Posso fare solo questo:

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

Supponiamo di avere un database contenente milioni e milioni di righe con un GUID come chiave primaria. Sarà sempre unico? Comprendo correttamente i GUID?

Ho letto questo articolo prima: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Mi confonde un po 'come sembra raccomandare un mezzo felice tra GUID e numeri interi come chiavi primarie.

Modifica l'11 / 06/18

Sono arrivato a credere che le guide siano più adatte degli ints per le mie esigenze. Sto usando CQRS di più in questi giorni e i GUID si adattano meglio.

Noto che alcuni sviluppatori modellano i GUID come stringhe nel modello di dominio, ad esempio qui: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - in questo caso: IdentityGuid è un GUID modellato come una stringa. C'è qualche motivo per farlo oltre a quanto indicato qui: utilizzare un oggetto valore personalizzato o un Guid come identificatore di entità in un sistema distribuito? . È "normale" modellare il GUID come stringa o devo modellarlo come GUID nel modello e nel database?



7
Non è garantito per essere unico, anche se è improbabile che tu possa mai vedere una collisione. stackoverflow.com/questions/1155008/how-unique-is-uuid/...
icirellik

2
vedi anche: UUID collisioni
moscerino

2
Vedi anche dba.stackexchange.com/questions/54690/… , così come molte altre domande: questo argomento è stato posto, risposto e discusso spesso.
Greenstone Walker,

1
Il sistema con cui sto lavorando al momento utilizza gli UUID. Una proprietà interessante è che un ID identifica in modo univoco un record, al contrario di un ID sequenziale che identifica un record in quella tabella.
Giustino,

Risposte:


41

I GUID sono per definizione "IDentificatori univoci globali". C'è un concetto simile ma leggermente diverso in Java chiamato UUID "IDentificatori universalmente unici". I nomi sono intercambiabili per tutti gli usi pratici.

I GUID sono fondamentali per il modo in cui Microsoft ha previsto il funzionamento del clustering di database e, se è necessario incorporare dati da origini a volte connesse, aiutano davvero a prevenire le collisioni di dati.

Alcuni fatti Pro-GUID:

  • I GUID impediscono le collisioni chiave
  • I GUID aiutano a unire i dati tra reti, macchine, ecc.
  • SQL Server supporta i GUID semi-sequenziali per ridurre al minimo la frammentazione dell'indice ( ref , alcuni avvertimenti)

Un po 'di bruttezza con i GUID

  • Sono grandi, 16 byte ciascuno
  • Sono fuori servizio, quindi non è possibile ordinare l'ID e sperare di ottenere l'ordine di inserzione come è possibile sugli ID con incremento automatico
  • Sono più ingombranti con cui lavorare, in particolare su piccoli set di dati (come le tabelle di ricerca)
  • La nuova implementazione GUID è più affidabile su SQL Server di quanto non lo sia nella libreria C # (puoi avere GUIDS sequenziali da SQL Server, in C # è casuale)

I GUID aumenteranno gli indici, quindi il costo dello spazio su disco per l'indicizzazione di una colonna sarà maggiore. I GUID casuali frammenteranno i tuoi indici.

Se sai che non sincronizzerai i dati di reti diverse, i GUID possono comportare un sovraccarico maggiore di quello che valgono.

Se hai bisogno di ingerire dati da client a volte connessi, possono essere molto più robusti per prevenire le collisioni chiave che fare affidamento sull'impostazione di intervalli di sequenza per quei client.


18
La mia comprensione è che i GUID sono sinonimo di UUID. UUID è il nome standard. GUID è ciò che Microsoft li ha coniati prima di RFC 4122 .
JimmyJames,

13
"Sono fuori servizio, quindi non è possibile ordinare l'ID e sperare di ottenere l'ordine di inserimento come è possibile con gli ID con incremento automatico" Francamente, non mi sento a mio agio a fare affidamento su quello con ID regolari. Anche se è possibile in un caso limite estremo che un ID inferiore si impegni sul disco in un secondo momento, preferirei fare affidamento su dati di ordinamento utili, come un timestamp di inserimento. Gli ID devono essere trattati come indirizzi di memoria: tutto ne ha uno, ma il valore stesso non ha senso. Usali al massimo per i tiebreakers. Soprattutto perché se hai un carico di massa, l'ordine di inserimento non è garantito.
Clockwork-Muse

8
@CortAmmon Secondo Wikipedia e RFC 4122 , sono sinonimi. P. Leach di Microsoft è stato uno dei creatori di RFC. Penso che da quando è stato creato l'RFC, i due sono gli stessi. Dall'RFC: "UUID (Universally Unique IDentifier), noto anche come GUID (Globally Unique IDentifier)." Penso che sia anche utile notare che i GUID non sono stati creati da MS. Hanno appena creato un nuovo nome per una tecnologia adottata altrove.
JimmyJames,

6
"SQL Server ha ottimizzazioni per la gestione dei GUID, quindi non dovrebbe influire molto sulle prestazioni delle query." -1 Non abbastanza ottimizzato. Sto lavorando con un DB in cui tutte le PK sono guide ed è una delle cause principali di scarse prestazioni.
Andy,

7
"SQL Server ha ottimizzazioni per la gestione dei GUID, quindi non dovrebbe influire molto sulle prestazioni della query. " Non vero. Tale affermazione presuppone che altri tipi di dati non siano ottimizzati. I server di database hanno anche ottimizzazioni per gestire semplici valori int, ad esempio. I GUID / UUID sono molto più lenti rispetto all'utilizzo di un valore int a 4 byte. 16 byte non saranno mai veloci come 4 byte, specialmente su una macchina che gestisce al massimo 4 o 8 byte in modo nativo.
Andrew Henle,

28

Sarà sempre unico?

Sempre? no, non sempre; è una sequenza finita di bit.

Supponiamo di avere un database contenente milioni e milioni di righe con un GUID come chiave primaria.

Milioni e milioni, probabilmente sei al sicuro. Un milione di milioni e la probabilità di una collisione diventa significativa. Ci sono buone notizie, però: hai già esaurito lo spazio su disco prima che accada.

Posso fare solo questo?

Puoi; non è una buona idea. Il tuo modello di dominio non dovrebbe normalmente generare numeri casuali; dovrebbero essere input per il tuo modello.

Inoltre, quando hai a che fare con una rete inaffidabile, dove potresti ricevere messaggi duplicati, un UUID generato in modo deterministico ti proteggerà dall'avere entità duplicate. Ma se assegni un nuovo numero casuale a ciascuno, allora hai più lavoro da fare per identificare la duplicazione.

Vedere la descrizione di uuid basato sul nome in RFC 4122

È "normale" modellare il GUID come stringa o dovrei modellarlo come GUID nel modello e nel database?

Non penso che importi molto. Per la maggior parte del modello di dominio, è un identificatore ; l'unica domanda che ti chiedi è se è uguale o meno a qualche altro identificatore. Il tuo modello di dominio normalmente non esaminerà la rappresentazione in memoria di un identificatore.

Se il GUID è disponibile come "tipo primitivo" nell'impostazione agnostica del tuo dominio, lo userei; consente al contesto di supporto di scegliere le ottimizzazioni appropriate che potrebbero essere disponibili.

Ciò che dovresti riconoscere, tuttavia, è che la rappresentazione dell'identificatore, sia in memoria che in memoria, è una decisione che stai prendendo nella tua implementazione, e quindi dovresti prendere provvedimenti per garantire che l'impronta del codice abbinata a quella la decisione è piccola - vedi Parnas 1972 .


20
+1 per "hai già esaurito lo spazio su disco per il tempo che accade".
w0051977,

2
Sento che il concetto di " UUID generato deterministicamente " è essenziale (vedi Data Vault 2)
alk

In effetti, essere in grado di ricalcolare un UUID / GUID sulla base di altri dati è un aiuto immenso, specialmente per rilevare duplicati. Una volta ho creato un sistema di elaborazione dei messaggi che memorizzava i messaggi e li spingeva attraverso una pipeline di elaborazione. Ho creato un hash del messaggio e l'ho usato come chiave primaria in tutto il sistema. solo che, di per sé, mi ha risolto MOLTI problemi per identificare il messaggio quando abbiamo dovuto ridimensionare.
Newtopian,

Un milione di = 2 ^ 40. Ciò crea 2 ^ 79 coppie di possibili collisioni. Il GUID ha 2 ^ 128 bit, quindi la possibilità è uno su 2 ^ 49. È molto più probabile che tu abbia un bug che riutilizza lo stesso GUID per due record o che crede erroneamente che ci sia una collisione in cui non ce n'è.
gnasher729

Sto tornando indietro attraverso le mie domande storiche. Prima di accettare; potresti dare un'occhiata alla mia modifica?
w0051977,

11

Il GUID o l' UUID sarà molto probabilmente unico a causa del modo in cui sono generati e forniscono un modo sicuro per garantire l'univocità senza dover comunicare con un'autorità centrale.

Vantaggi dei GUID come chiave primaria:

  • È possibile copiare dati tra diversi frammenti di un cluster e non è necessario preoccuparsi delle collisioni PK.
  • Ti consente di conoscere la tua chiave primaria prima di aver inserito qualsiasi record.
  • Semplifica la logica di transazione per l'inserimento di record figlio.
  • Non può essere facilmente indovinato.

Nell'esempio fornito:

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

Se si specifica il GUID prima del tempo di inserimento, è possibile salvare un round trip nel database durante l'inserimento di record figlio successivi e consentire di impegnarli nella stessa transazione.

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

Detrimenti ai GUID come chiave primaria:

  • Sono grandi 16 byte, il che significa che consumeranno più spazio man mano che vengono aggiunti indici e chiavi esterne.
  • Non ordinano bene in quanto sono essenzialmente numeri casuali.
  • L'utilizzo dell'indice è molto, molto, molto negativo.
  • Molta foglia in movimento.
  • Sono difficili da ricordare.
  • Sono difficili da verbalizzare.
  • Possono rendere l'URL più difficile da leggere.

Se l'applicazione non ha bisogno di sharding o cluster, sarebbe meglio attenersi a tipi di dati più piccoli e semplici come int o bigint.

Molti database hanno implementazioni interne che tentano di mitigare i problemi di archiviazione causati da GUID e SQL Server ha anche una funzione newsequentialid per aiutare con l'ordinamento di UUID che consente un migliore utilizzo degli indici e generalmente hanno migliori prestazioni.

Inoltre, dal punto di vista di un tester, utente o sviluppatore che lavora con l'applicazione, l'utilizzo di un ID su un GUID migliorerà in modo significativo la comunicazione. Immagina di dover leggere un GUID al telefono.

Alla fine, a meno che il clustering su larga scala o gli URL offuscati non siano un requisito, è più pragmatico attenersi agli ID auto-incrementanti.


1
Una cosa da considerare è che a seconda del tipo di UUID , contengono informazioni che potrebbero essere potenzialmente utilizzate per identificare la macchina su cui sono generate. La variante casuale pura può avere maggiori probabilità di scontrarsi senza entropia sufficiente. Questo dovrebbe essere considerato prima dell'uso in un URI.
JimmyJames,

D'accordo, anche se non si dovrebbe mai esporre la propria chiave primaria in un URL. È necessario utilizzare un metodo più appropriato per garantire che non vi siano perdite di dati sicuri su un sistema esterno.
icirellik

1
C'è un altro caso d'uso: database OLTP con inserimento pesante in cui il blocco per la sequenza è un collo di bottiglia. Secondo il mio amico Oracle DBA, questo non è così raro come sembra, non hai nemmeno bisogno di grandi dimensioni o cluster per quello. • Alla fine, appesantire i pro ei contro (e non confondere i pro / contro degli UUID con i pro / contro che non sono specifici degli UUID come fanno alcuni poster) e misurare .
mirabilos,

1
Se usi newsequentialid allora devi andare nel db per ottenere l'id (come con un'identità int), vero? Qual è il vantaggio qui.
w0051977,

1
@mirabilos Per essere chiari, quando dico terribile abbiamo finito per avere inserti che richiedevano minuti per riga. È iniziato OK ma dopo che c'erano decine di migliaia di file, è andato di lato molto velocemente. Se non è ovvio, 10s di migliaia di righe è una tabella molto piccola.
JimmyJames,

4

Direi di no, non usare i GUID come chiavi primarie. Attualmente ho a che fare con un DB di questo tipo e sono una delle principali cause di problemi di prestazioni.

I 12 byte extra si sommano rapidamente; ricorda, la maggior parte dei PK saranno FK in altre tabelle, e solo tre FK in una tabella ora hai 48 byte in più per ogni riga. Ciò si somma nella tabella e negli indici. Si aggiunge anche all'I / O del disco. Quei 12 byte extra devono essere letti e scritti.

E se non stai usando guide sequenziali e i PK sono raggruppati (cosa che succede di default), di tanto in tanto SQL dovrà spostare intere pagine di dati per spremere di più nel giusto "punto". Per un database altamente transazionale con molti inserti, aggiornamenti ed eliminazioni, le cose si impantanano rapidamente.

Se hai bisogno di un tipo di identificatore univoco per la sincronizzazione o qualcosa del genere, aggiungi una colonna guid. Basta non renderlo il PK.


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

Questo è di gran lunga il motivo più importante per l'utilizzo dei GUID.

Il fatto che sia possibile creare un ID univoco senza che il codice sia a conoscenza o che comunichi con il proprio livello di persistenza è un enorme vantaggio.

Puoi essere sicuro che l'oggetto Person che hai appena generato sul tuo server, telefono pc, laptop, dispositivo offline o qualunque cosa sia unica in tutti i tuoi server in tutto il mondo, comunque distribuito.

Puoi attaccarlo in qualsiasi tipo di database rdb o no-sql, file, inviarlo a qualsiasi servizio web o buttarlo via immediatamente come non necessario

No non avrai mai una collisione.

Sì, gli inserti possono essere leggermente più lenti poiché potrebbe essere necessario manipolare l'indice.

Sì, è più grande di un int.

  • modificare. ho dovuto sparare prima di finire.

So che molte persone si sentono fortemente in merito agli autoincrets e questo è un argomento controverso con i DBA

Ma non posso davvero affermare abbastanza forte quanto siano superiori le guide. È necessario utilizzare le guide per impostazione predefinita in qualsiasi applicazione.

gli ingressi automatici presentano molti difetti

  • Si utilizza un db distribuito No-Sql. Non puoi semplicemente parlare con tutte le altre istanze per scoprire qual è il prossimo numero.

  • Si utilizza un sistema di coda messaggi. Le cose hanno bisogno di ID prima che colpiscano il db

  • Stai creando diversi elementi e modificandoli prima di salvarli. Ognuno ha bisogno di un ID prima di aver colpito il db

  • Si desidera eliminare e reinserire le righe. Assicurati di non contare i tuoi ID auto ed esaurirti!

  • Vuoi non esporre quanti ordini hai preso quest'anno per ogni utente

  • Volete spostare i dati anonimi dalla produzione al test e mantenere intatte le relazioni. Ma non eliminare tutti i dati di test esistenti.

  • Desideri unire il tuo singolo prodotto tenant in un database multi-tenant ma tutti hanno un ordine 56.

  • Si creano oggetti persistenti ma effimeri. (ordini incompleti) di nuovo, non consumare tutti i tuoi contenuti con oggetti che non esistono più.

L'elenco è infinito e sono tutti problemi reali che accadono continuamente alle persone. a differenza di esaurire lo spazio su disco a causa di cols FK leggermente più grandi

Finalmente il grosso problema con gli ints è che li hai esauriti !!! ok in teoria non lo fai, ci sono un sacco. Ma in pratica lo fai perché le persone non li trattano come numeri casuali senza significato. fanno cose come

  • oh, non voglio che i clienti pensino che siamo nuovi. inizia alle 10.000

  • Ho dovuto importare un carico di dati, quindi ho appena aumentato il seed a 1m in modo da sapere cosa viene importato

  • abbiamo bisogno di categorie di dati. ogni periodo inizia al prossimo milione in modo che possiamo usare le prime cifre come un numero magico

  • Ho cancellato e reimportato di nuovo tutti i dati con nuovi ID. Sì, anche i registri di controllo.

  • usa questo numero, che è una chiave composita, come ID di quest'altra cosa


1
Non c'è nulla di veramente sbagliato in questa risposta, ma vorrei (per evitare ulteriori downvotes) forse rendere esplicito l'avvertimento che anche se le applicazioni della vita reale non incontreranno collisioni, è teoricamente possibile. (O forse oltre 45 database exabyte sono più diffusi di quanto pensassi ...). Anche se penso che il linguaggio "la ragione più importante" sia un po 'forte, questo è ciò che trovo più utile.
BurnsBA

2
è più probabile che un incidente automobilistico si scontrerà di un guid
Ewan,

4
-1 per "È necessario utilizzare le guide per impostazione predefinita in qualsiasi applicazione." Dipende ™. E come altri hanno dimostrato, i GUID / UUID non sono assolutamente garantiti come unici.
Max Vernon,

3
Le risposte "dipende" sono inutili, certo che ci saranno alcune strane applicazioni in cui un int è migliore. Ma è probabile che la tua applicazione non sia una di queste. I GUID sono la cosa più unica che puoi ottenere
Ewan,

2
Penso che ci saranno alcune strane applicazioni in cui le guide sono migliori. Unico non è la cosa più importante da considerare. I tuoi "difetti" di ints sono massicciamente esagerati e non consideri nessuno dei tanti aspetti negativi delle guide.
Andy,

2

Mi rendo conto che questi GUID vengono utilizzati per identificare oggetti a livello di applicazione. Sono anche memorizzati come chiave primaria a livello di database.

Ecco dove dovresti fermarti, proprio lì, e ripensare.

La chiave primaria del database non dovrebbe MAI avere significato commerciale. Dovrebbe essere privo di significato per definizione.

Quindi aggiungi il GUID come chiave aziendale e una normale chiave primaria (di solito un int lungo) come chiave primaria del database. È sempre possibile inserire un indice univoco nel GUID per garantire l'univocità.

Ovviamente sta parlando della teoria del database, ma è anche una buona pratica. Ho avuto a che fare con database in cui le chiavi primarie avevano un significato commerciale (un cliente aveva pensato di risparmiare alcune risorse di database usandole ad esempio come numeri di dipendenti, numeri di clienti, ecc. Ecc.) E ciò crea sempre problemi.


1
In che modo differisce dall'interrogare dal livello applicazione usando una chiave primaria intera? A quel punto, viene anche utilizzato per identificare oggetti a livello dell'applicazione. È necessario un modo per identificare gli oggetti in un database dal livello dell'applicazione.
Icirellik,

@icirellik la chiave primaria è pensata per l'uso interno da parte del database, per collegare i record padre e figlio e simili. NON è pensato per l'uso da parte della logica dell'applicazione, per questo usi ID aziendali, come un numero di prodotto o un nome.
jwenting

2

Utilizzare sempre chiavi primarie (PK) generate automaticamente dal database.

Perché utilizzare l'auto-incrementing anziché GUID / UUID?

  • I GUID (UUID) non impediscono le collisioni chiave poiché non sono univoci e non è possibile renderli unici poiché generati da numerose fonti.
  • I GUID non aiutano con l'unione poiché aumentano notevolmente il processo di unione già dispendioso in termini di tempo con colonne PK e FK estremamente lunghe e non intere che impiegano molto tempo per l'elaborazione. Ricorda che per la maggior parte dei PK, ci sarà almeno un'altra tabella con almeno 2 chiavi della stessa dimensione: è la sua PK e un FK di nuovo alla prima tabella. Tutti devono essere risolti in una fusione.

Ma come gestire i frammenti, i cluster, ecc.?

  • Crea PK multi-colonna costituiti da colonne separate che identificano ogni frammento / cluster / database / qualunque cosa gestisca le proprie chiavi auto-incrementanti. Per esempio...

Un PK a 3 colonne per una tabella in cluster potrebbe essere ...

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

Ma per quanto riguarda...?

  • Viaggi multipli nel database - La maggior parte delle applicazioni non ha bisogno di identificare in modo univoco un record creato fino a quando non viene inserito nel database poiché quel thread / sessione / qualunque cosa stia funzionando solo uno alla volta. Se l'applicazione ha davvero bisogno di questa capacità, utilizzare un'applicazione PK temporanea generata dall'applicazione che non viene inviata al database . Consentire quindi al database di inserire il proprio PK di incremento automatico nella riga quando viene inserito. Gli inserti utilizzeranno il PK temporaneo, mentre gli aggiornamenti e le eliminazioni utilizzeranno il PK permanente assegnato dal database.

  • Prestazioni - I computer possono elaborare interi semplici molto più velocemente di qualsiasi altra cosa a causa del dominio molto più grande se possibile valori per elemento in un GUID (37) rispetto a un intero (10). Ricorda anche che ogni carattere in un GUID deve essere prima convertito in un numero per essere manipolato dalla CPU.

Gli abusi comuni delle chiavi primarie I PK hanno un solo scopo ... identificare in modo assolutamente univoco una riga in una tabella. Qualcos'altro è un abuso fin troppo comune.

Rilevamento di record mancanti

  • I record mancanti non possono essere rilevati guardando i PK. Benedici il QA per almeno tentare di garantire la qualità dei dati. Tuttavia, loro e la mancanza di comprensione da parte del programmatore del modo in cui vengono assegnate le chiavi nei moderni sistemi di database spesso li portano all'incredulità che un numero mancante in un PK a incremento automatico significhi dati mancanti. Lo fa non perché ...
  • Per quanto riguarda le prestazioni, i sistemi di database assegnano blocchi di numeri in "sequenze" (batch, intervalli) per ridurre al minimo i viaggi al database effettivo in memoria. La dimensione di queste sequenze di numeri è spesso sotto il controllo del DBA ma potrebbe non essere sintonizzabile in base alla tabella.
  • La chiave da asporto è ... i numeri non utilizzati da queste sequenze non vengono mai restituiti al database, quindi ci sono sempre lacune nei numeri PK.
  • Perché ci dovrebbero essere numeri inutilizzati che chiedi? Perché una varietà di azioni di manutenzione del database può causare l'abbandono delle sequenze. Si tratta di riavvii, ricariche di massa di tabelle, alcuni tipi di ripristino da backup e altre operazioni.

Ordinamento

  • L'ordinamento per PK è molto soggetto a errori poiché la maggior parte delle persone penserà che elenca le righe nell'ordine in cui sono state create e che corrisponde al tempo di clock. Principalmente, ma non necessariamente.
  • I motori di database sono ottimizzati per le massime prestazioni e ciò può significare ritardare l'inserimento dei risultati di una transazione complicata di lunga durata al fine di inserire quelli semplici e brevi, "fuori turno" per così dire.

Cosa ne pensi dello schema delle tabelle in modo tale che l'unica colonna univoca sia una chiave primaria a incremento automatico creata dal database? Soprattutto per le tabelle che non hanno una chiave esterna ma la cui chiave primaria è la chiave esterna per diverse tabelle correlate?
RibaldEddie,

Ho aggiunto molto di più alla risposta in tal senso. La risposta originale era incompleta a causa dell'app Android SE che sto impiccando. Penso che sia in fase di sviluppo un'importante riscrittura dell'app.
DocSalvager,

Quindi secondo te sarebbe giusto che una tabella contenga un numero qualsiasi di righe identiche, salvo per la chiave primaria a incremento automatico?
RibaldEddie,

@RibaldEddie - Per quanto riguarda ciò che il DB è progettato per consentire ... assolutamente. Le eliminazioni sono facili. Quando si verifica il tuo scenario, lo considero un bug da correggere nel software e quindi eliminare entrambe le righe. Il caso molto più comune è che due record per la stessa cosa con dati leggermente diversi, quindi devono essere uniti. Se una colonna è vuota in un record e ha un valore nell'altro, la scelta è ovvia e può essere automatizzata. Spesso il datetimestamp può essere utilizzato per arbitrare una fusione automatizzata. Alcuni duplicati richiedono che una persona completi e verifichi l'unione in base alle regole aziendali.
DocSalvager il

1

Come qualsiasi cosa, ci sono vantaggi e svantaggi nel fare questo:

Il bene:

  1. Le tue chiavi hanno sempre la stessa lunghezza (database molto grandi possono avere chiavi molto grandi)

  2. L'unicità è praticamente garantita - anche quando li stai generando da un sistema separato e / o non hai letto l'ultimo ID dal database

Il cattivo:

  1. Come accennato molto sopra - indici e archivio dati più grandi.

  2. Non puoi ordinare per ID, devi ordinare per qualcos'altro. Più indici, probabilmente meno efficienti.

  3. Sono meno leggibili dall'uomo. I numeri interi sono generalmente più facili da analizzare, ricordare e digitare per le persone. L'uso dei GUID come ID nelle clausole WHERE su più tabelle unite può far sciogliere la testa.

Come ogni cosa, usali dove appropriato, non essere dogmatico - in molte situazioni gli interi con incremento automatico sono migliori, a volte i GUID sono fantastici.


0

Sì, puoi utilizzare GUID come chiave primaria. Il lato negativo è la dimensione e la rapida frammentazione dell'indice.

A meno che non sia necessaria l'univocità tra i database (ad esempio un cluster), si preferisce un numero intero.


I generatori GUID possono produrre lo stesso GUID più di una volta, in ciò risiede un difetto. Se lo faranno o meno dipenderà dalla loro granularità, principalmente dall'intervallo tra i segni di spunta dell'orologio. Ad esempio, un generatore di clock può spuntare solo ogni 100ms, portando a 2 GUID richiesti entro quei 100ms su quella macchina identici. Esistono modi per evitarlo, principalmente, ma molti generatori GUID funzionano completamente al di fuori dell'indirizzo IP e / o dell'indirizzo MAC e di un timestamp.
jwenting

0

Ecco la mia opinione su questo problema: la soluzione è a metà strada tra i valori GUID e int, prendendo il meglio da entrambi.

La classe genera un valore ID pseudo casuale (ma crescente nel tempo), che è simile a un GUID di Comb .

Il vantaggio principale è che consente di generare valori ID sul client, anziché utilizzare valori di incremento automatico generati sul server (che richiede un round trip) con un rischio quasi zero di valori duplicati.

I valori generati utilizzano solo 8 byte anziché 16 per un GUID e non dipendono da uno specifico ordinamento del database (ad esempio Sql Server per GUID ). I valori potrebbero essere espansi per utilizzare l'intero intervallo senza segno, ma ciò causerebbe problemi con qualsiasi database o altro repository di dati che ha solo tipi interi con segno.

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;
    }
}
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.