Verwenden einer GUID als Primärschlüssel

32

Ich verwende in der Regel Auto-Inkrement-IDs als Primärschlüssel in Datenbanken. Ich versuche, die Vorteile der Verwendung von GUIDs zu lernen. Ich habe diesen Artikel gelesen: https://betterexplained.com/articles/the-quick-guide-to-guids/

Mir ist klar, dass diese GUIDs verwendet werden, um Objekte auf Anwendungsebene zu identifizieren. Werden sie auch als Primärschlüssel auf Datenbankebene gespeichert? Angenommen, ich hatte die folgende Klasse:

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

//Person Methods follow
}

Angenommen, ich wollte eine neue Person im Speicher erstellen und dann die Person in eine Datenbank einfügen. Kann ich das einfach machen:

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

Angenommen, ich hatte eine Datenbank mit Millionen und Abermillionen von Zeilen mit einer GUID als Primärschlüssel. Wird das immer einzigartig sein? Verstehe ich überhaupt GUIDs richtig?

Ich habe diesen Artikel früher gelesen: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Es verwirrt mich ein wenig, da es den Anschein hat, als würde ich ein fröhliches Medium zwischen GUIDs und Ganzzahlen als Primärschlüssel empfehlen.

Edit 11/06/18

Ich bin der Überzeugung, dass Guids für meine Anforderungen besser geeignet sind als Ints. Ich verwende CQRS heutzutage häufiger und GUIDs passen besser dazu.

Ich habe festgestellt, dass einige Entwickler die GUIDs als Zeichenfolgen im Domänenmodell modellieren, z. B. hier: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - in diesem Fall: IdentityGuid ist eine GUID, die als Zeichenfolge modelliert wird. Gibt es einen anderen Grund als den hier angegebenen: Verwenden Sie ein benutzerdefiniertes Wertobjekt oder eine Guid als Entitätskennung in einem verteilten System? . Ist es "normal", die GUID als Zeichenfolge zu modellieren, oder sollte ich sie als GUID in Modell und Datenbank modellieren?

w0051977
quelle
7
Nicht garantiert einzigartig, obwohl es unwahrscheinlich ist, dass es jemals zu einer Kollision kommt. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik
2
Siehe auch: UUID-Kollisionen
Mücke
2
Siehe auch dba.stackexchange.com/questions/54690/… sowie viele andere Fragen - dieses Thema wurde häufig gestellt, beantwortet und diskutiert.
Greenstone Walker
1
Das System, mit dem ich gerade arbeite, verwendet UUIDs. Eine nette Eigenschaft ist, dass eine ID einen Datensatz eindeutig identifiziert, im Gegensatz zu einer sequentiellen ID, die einen Datensatz in dieser Tabelle identifiziert.
Justin

Antworten:

41

GUIDs sind per Definition "Globally Unique IDentifiers". In Java gibt es ein ähnliches, aber leicht unterschiedliches Konzept mit der Bezeichnung UUIDs "Universally Unique IDentifiers". Die Namen sind für den praktischen Gebrauch austauschbar.

GUIDs sind von zentraler Bedeutung für die Funktionsweise von Microsoft-Datenbankclustern. Wenn Sie Daten aus manchmal verbundenen Quellen einbinden müssen, tragen sie wirklich dazu bei, Datenkollisionen zu vermeiden.

Einige Pro-GUID-Fakten:

  • GUIDs verhindern Schlüsselkollisionen
  • GUIDs helfen beim Zusammenführen von Daten zwischen Netzwerken, Computern usw.
  • SQL Server bietet Unterstützung für semi-sequentielle GUIDS zu Hilfe Index - Fragmentierung minimieren ( ref , einige Einsprüche)

Etwas hässlich mit GUIDs

  • Sie sind groß, jeweils 16 Bytes
  • Sie sind nicht in Ordnung, daher können Sie die ID nicht sortieren und hoffen, die Einfügereihenfolge wie bei der automatischen Inkrementierung von IDs zu erhalten
  • Es ist umständlicher, mit ihnen zu arbeiten, insbesondere bei kleinen Datenmengen (wie Nachschlagetabellen).
  • Die neue GUID-Implementierung ist unter SQL Server robuster als in der C # -Bibliothek (Sie können sequenzielle GUIDs von SQL Server verwenden, in C # ist dies zufällig).

Durch GUIDs werden Ihre Indizes größer, sodass die Speicherplatzkosten für die Indizierung einer Spalte höher sind. Zufällige GUIDs fragmentieren Ihre Indizes.

Wenn Sie nicht wissen, dass Sie Daten aus verschiedenen Netzwerken synchronisieren, können GUIDs mehr Aufwand verursachen, als sie wert sind.

Wenn Sie Daten von manchmal verbundenen Clients erfassen müssen, sind diese möglicherweise wesentlich robuster, um Schlüsselkollisionen zu verhindern, als wenn Sie Sequenzbereiche für diese Clients festlegen.

Berin Loritsch
quelle
18
Nach meinem Verständnis sind GUIDs gleichbedeutend mit UUIDs. UUID ist der Standardname. GUID ist das, was Microsoft vor RFC 4122 geprägt hat .
JimmyJames
13
"Sie sind nicht in Ordnung, Sie können also nicht nach ID sortieren und hoffen, die Einfügereihenfolge wie bei der automatischen Inkrementierung von IDs zu erhalten." Während es im Extremfall möglich ist, dass eine niedrigere ID später auf die Festplatte geschrieben wird, würde ich mich lieber auf nützliche Sortierdaten wie einen Einfügezeitstempel verlassen. IDs sollten wie Speicheradressen behandelt werden - alles hat eine, aber der Wert selbst ist bedeutungslos. Verwenden Sie sie höchstens für Tiebreaker. Vor allem, wenn Sie eine Bulkladung haben, ist die Einfügereihenfolge nicht garantiert.
Clockwork-Muse
8
@CortAmmon Laut Wikipedia und RFC 4122 sind sie synonym. P. Leach von Microsoft war einer der Schöpfer des RFC. Ich denke, seit der RFC erstellt wurde, sind die beiden identisch. Aus dem RFC: "UUIDs (Universally Unique IDentifier), auch bekannt als GUIDs (Globally Unique IDentifier)." Ich denke, es ist auch nützlich zu beachten, dass GUIDs nicht von MS erstellt wurden. Sie haben gerade einen neuen Namen für eine Technologie geschaffen, die von einer anderen Stelle übernommen wurde.
JimmyJames
6
"SQL Server verfügt über Optimierungen für den Umgang mit GUIDs, sodass die Abfrageleistung nicht wesentlich beeinträchtigt werden sollte." -1 Nicht annähernd optimiert genug. Ich arbeite mit einer Datenbank, in der alle PKs Guids sind und deren Hauptursache eine schlechte Leistung ist.
Andy
7
"SQL Server verfügt über Optimierungen für den Umgang mit GUIDs, sodass die Abfrageleistung nicht wesentlich beeinträchtigt werden sollte. " Diese Anweisung geht davon aus, dass andere Datentypen nicht optimiert sind. Datenbankserver verfügen beispielsweise auch über Optimierungen für den Umgang mit einfachen int-Werten. GUIDs / UUIDs sind viel langsamer als die Verwendung eines 4-Byte-Int-Werts. 16 Bytes sind nie so schnell wie 4 Bytes - insbesondere auf einem Computer, der nativ höchstens 4 oder 8 Bytes verarbeitet.
Andrew Henle
28

Wird das immer einzigartig sein?

Immer? Nein nicht immer; Es ist eine endliche Folge von Bits.

Angenommen, ich hatte eine Datenbank mit Millionen und Abermillionen von Zeilen mit einer GUID als Primärschlüssel.

Millionen und Abermillionen, Sie sind wahrscheinlich in Sicherheit. Eine Million Millionen, und die Wahrscheinlichkeit einer Kollision wird erheblich. Es gibt jedoch gute Nachrichten: Bis dahin ist der Speicherplatz bereits erschöpft.

Kann ich das einfach machen?

Sie können; Es ist keine ganz gute Idee. Ihr Domain-Modell sollte normalerweise keine Zufallszahlen generieren. Sie sollten Eingaben für Ihr Modell sein.

Darüber hinaus schützt Sie eine deterministisch generierte UUID in einem unzuverlässigen Netzwerk, in dem möglicherweise doppelte Nachrichten angezeigt werden, vor doppelten Entitäten. Wenn Sie jedoch jedem eine neue Zufallszahl zuweisen, müssen Sie mehr tun, um die Duplizierung zu identifizieren.

Siehe die Beschreibung der namensbasierten UUID in RFC 4122

Ist es "normal", die GUID als Zeichenfolge zu modellieren, oder sollte ich sie als GUID in Modell und Datenbank modellieren?

Ich denke nicht, dass es sehr wichtig ist. Bei den meisten Domain-Modellen handelt es sich um eine Kennung . Die einzige Frage, die Sie danach stellen, ist, ob sie mit einer anderen Kennung identisch ist oder nicht. In Ihrem Domain-Modell wird normalerweise nicht die speicherinterne Darstellung eines Bezeichners überprüft.

Wenn GUID in Ihrer Domain-Agnostic-Einstellung als "primitiver Typ" verfügbar ist, würde ich es verwenden. es ermöglicht dem unterstützenden Kontext, geeignete Optimierungen auszuwählen, die verfügbar sein können.

Was Sie jedoch erkennen sollten, ist, dass die Darstellung des Bezeichners sowohl im Speicher als auch im Speicher eine Entscheidung ist, die Sie in Ihrer Implementierung treffen, und daher sollten Sie Schritte unternehmen, um sicherzustellen, dass der Footprint des Codes damit gekoppelt ist Entscheidung ist klein - siehe Parnas 1972 .

VoiceOfUnreason
quelle
20
+1 für "Sie haben zu diesem Zeitpunkt bereits keinen Speicherplatz mehr."
w0051977
2
Ich halte das Konzept der " deterministisch generierten UUID " für wesentlich (siehe Datentresor 2)
alk
In der Tat ist die Möglichkeit, eine UUID / GUID auf der Grundlage anderer Daten neu zu berechnen, eine immense Hilfe, insbesondere zum Erkennen von Duplikaten. Ich habe einmal ein Nachrichtenverarbeitungssystem aufgebaut, in dem die Nachrichten gespeichert und über eine Verarbeitungspipeline übertragen wurden. Ich habe einen Hash der Nachricht erstellt und diesen als Primärschlüssel im gesamten System verwendet. Nur so konnte ich eine Menge Probleme lösen, um die Botschaft zu identifizieren, als wir ausbauen mussten.
Newtopian
Eine Million Millionen = 2 ^ 40. Das ergibt 2 ^ 79 mögliche Kollisionspaare. GUID hat 2 ^ 128 Bits, die Chance ist also eins zu 2 ^ 49. Es ist viel wahrscheinlicher, dass Sie einen Fehler haben, der dieselbe GUID für zwei Datensätze wiederverwendet, oder der fälschlicherweise glaubt, dass es eine Kollision gibt, bei der es keine gibt.
gnasher729
Ich gehe auf meine historischen Fragen zurück. Bevor ich akzeptiere; Könntest du dir meine Bearbeitung ansehen?
w0051977
11

Die GUID oder UUID ist aufgrund ihrer Generierung sehr wahrscheinlich eindeutig und bietet eine sichere Möglichkeit, die Eindeutigkeit zu gewährleisten, ohne mit einer zentralen Behörde kommunizieren zu müssen.

Vorteile von GUIDs als Primärschlüssel:

  • Sie können Daten zwischen verschiedenen Shards eines Clusters kopieren und müssen sich keine Gedanken über PK-Kollisionen machen.
  • Damit können Sie Ihren Primärschlüssel kennen, bevor Sie Datensätze eingefügt haben.
  • Vereinfacht die Transaktionslogik zum Einfügen von untergeordneten Datensätzen.
  • Kann nicht leicht erraten werden.

In dem von Ihnen angegebenen Beispiel:

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

Wenn Sie die GUID vor dem Einfügen angeben, kann beim Einfügen aufeinanderfolgender untergeordneter Datensätze ein Roundtrip in die Datenbank gespeichert werden, und Sie können sie in derselben Transaktion festschreiben.

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

Nachteile für GUIDs als Primärschlüssel:

  • Sie sind 16 Byte groß, was bedeutet, dass sie mehr Speicherplatz beanspruchen, wenn Indizes und Fremdschlüssel hinzugefügt werden.
  • Sie sortieren nicht gut, da es sich im Wesentlichen um Zufallszahlen handelt.
  • Die Indexnutzung ist sehr, sehr, sehr schlecht.
  • Viel Blattbewegung.
  • Sie sind schwer zu merken.
  • Sie sind schwer zu verbalisieren.
  • Sie können das Lesen von URLs erschweren.

Wenn Ihre Anwendung kein Sharding oder Clustering benötigt, sollten Sie sich an kleinere, einfachere Datentypen wie int oder bigint halten.

Viele Datenbanken haben ihre eigenen internen Implementierungen, die versuchen, die durch GUIDs verursachten Speicherprobleme abzumildern. SQL Server verfügt sogar über eine neue sequentielle ID , um die Reihenfolge der UUIDs zu vereinfachen und eine bessere Verwendung der Indizes zu ermöglichen, und sie weisen im Allgemeinen bessere Leistungseigenschaften auf.

Aus der Sicht eines Testers, Benutzers oder Entwicklers, der mit der Anwendung arbeitet, verbessert die Verwendung einer ID über eine GUID die Kommunikation erheblich. Stellen Sie sich vor, Sie müssen eine GUID über ein Telefon lesen.

Letztendlich ist es pragmatischer, sich an automatisch inkrementierende IDs zu halten, es sei denn, es sind umfangreiche Clustering- oder Verschleierungs-URLs erforderlich.

icirellik
quelle
1
Eine zu berücksichtigende Sache ist, dass sie abhängig vom Typ der UUID Informationen enthalten, die möglicherweise zur Identifizierung des Computers verwendet werden können, auf dem sie generiert werden. Die reine Zufallsvariante kollidiert möglicherweise eher ohne ausreichende Entropie. Dies sollte vor der Verwendung in einem URI berücksichtigt werden.
JimmyJames
Einverstanden, obwohl man niemals seinen Primärschlüssel in einer URL offen legen sollte. Es sollte eine geeignetere Methode angewendet werden, um sicherzustellen, dass keine sicheren Daten in ein externes System gelangen.
icirellik
1
Es gibt noch einen weiteren Anwendungsfall: OLTP-Datenbanken mit umfangreichen Einfügungen, bei denen die Sperrung für die Sequenz ein Engpass ist. Laut meinem Oracle DBA-Freund ist dies nicht so selten, wie es sich anhört. Dazu benötigen Sie nicht einmal große Datenmengen oder Cluster. • Abwägen Sie am Ende die Vor- und Nachteile (und verwechseln Sie nicht die Vor- und Nachteile von UUIDs mit den Vor- und Nachteilen, die nicht wie bei einigen Postern für UUIDs spezifisch sind) und messen Sie .
Mirabilos
1
Wenn Sie newsequentialid verwenden, müssen Sie zur Datenbank gehen, um die ID zu erhalten (wie bei einer Identität int), nicht wahr? Was ist der Vorteil hier.
w0051977
1
@mirabilos Um ganz klar zu sein, wenn ich schrecklich sage, hatten wir schließlich Einfügungen, die pro Zeile Minuten in Anspruch nahmen . Es begann in Ordnung, aber nachdem es Zehntausende von Reihen gab, ging es sehr schnell zur Seite. Wenn es nicht offensichtlich ist, sind Zehntausende von Zeilen eine sehr kleine Tabelle.
JimmyJames
4

Ich würde nein sagen, keine GUIDs als Primärschlüssel verwenden. Ich habe gerade mit einer solchen Datenbank zu tun, und sie sind eine der Hauptursachen für Leistungsprobleme.

Die zusätzlichen 12 Bytes summieren sich schnell. Denken Sie daran, die meisten PKs sind FKs in anderen Tabellen, und nur drei FKs in einer Tabelle haben Sie jetzt 48 Byte mehr für jede Zeile. Das summiert sich in der Tabelle und in den Indizes. Es summiert sich auch in Festplatten-E / A. Diese zusätzlichen 12 Bytes müssen gelesen und geschrieben werden.

Und wenn Sie keine sequenziellen Guids verwenden und die PKs geclustert sind (was standardmäßig der Fall ist), muss SQL von Zeit zu Zeit ganze Seiten mit Daten verschieben, um sie an die richtige Stelle zu bringen. Bei einer hochgradig transaktionsbezogenen Datenbank mit vielen Einfügungen, Aktualisierungen und Löschvorgängen geht es schnell bergab.

Wenn Sie eine eindeutige Kennung für die Synchronisierung benötigen, fügen Sie eine Guid-Spalte hinzu. Mach es einfach nicht zum PK.

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

Dies ist mit Abstand der wichtigste Grund für die Verwendung von GUIDs.

Die Tatsache, dass Sie eine eindeutige ID erstellen können, ohne dass Ihr Code Ihre Persistenzschicht kennt oder mit dieser kommuniziert, ist ein großer Vorteil.

Sie können sicher sein, dass das soeben erstellte Personenobjekt auf Ihrem Server, PC-Telefon, Laptop, Offline-Gerät oder was auch immer auf all Ihren Servern auf der ganzen Welt einzigartig ist, jedoch verteilt.

Sie können es in jede Art von Datenbank rdb oder no-sql speichern, ablegen, an einen beliebigen Webservice senden oder es sofort als nicht benötigt wegwerfen

Nein, Sie werden niemals eine Kollision bekommen.

Ja, Einfügungen können etwas langsamer sein, da der Index möglicherweise geändert werden muss.

Ja, es ist größer als ein Int.

  • bearbeiten. musste abschießen, bevor er fertig wurde.

Ich weiß, dass viele Menschen Auto-Inc-Ints sehr ernst nehmen, und dies ist ein umstrittenes Thema bei DBAs

Aber ich kann wirklich nicht stark genug sagen, wie überlegen Guids sind. Sie sollten in jeder Anwendung standardmäßig Guids verwenden .

Auto Inc Ints haben viele, viele Mängel

  • Sie verwenden eine verteilte No-Sql-Datenbank. Sie können einfach nicht mit allen anderen Instanzen sprechen, um herauszufinden, was die nächste Nummer ist.

  • Sie verwenden ein Message Queue-System. Dinge brauchen Ausweise, bevor sie die Datenbank erreichen

  • Sie erstellen mehrere Elemente und bearbeiten sie vor dem Speichern. Jeder braucht eine ID, bevor Sie die DB getroffen haben

  • Sie möchten Zeilen löschen und erneut einfügen. Stellen Sie sicher, dass Sie Ihre Auto-Inc-IDs nicht hochzählen und leer sind!

  • Sie möchten nicht jedem Benutzer mitteilen, wie viele Bestellungen Sie in diesem Jahr entgegengenommen haben

  • Sie möchten anonymisierte Daten aus der Produktion verschieben, um die Beziehungen zu testen und aufrechtzuerhalten. Löschen Sie jedoch nicht alle vorhandenen Testdaten.

  • Sie möchten Ihr Produkt mit einem Mandanten in einer Datenbank mit mehreren Mandanten zusammenführen, aber jeder hat eine Bestellung 56.

  • Sie erstellen Objekte, die dauerhaft, aber kurzlebig sind. (unvollständige Bestellungen) Verbrauchen Sie nicht alle Ihre Bestellungen mit Dingen, die nicht mehr existieren.

Die Liste ist endlos und es handelt sich um echte Probleme, die den Menschen ständig passieren. im gegensatz zu nicht genügend speicherplatz wegen etwas größerer fk cols

Schließlich ist die massive Ausgabe mit ints, dass Sie aus ihnen heraus laufen !!! ok in der Theorie gibt es keine Lasten. Aber in der Praxis tun Sie das, weil die Leute sie nicht wie bedeutungslose Zufallszahlen behandeln. Sie machen Dinge wie

  • Oh, ich möchte nicht, dass Kunden glauben, wir seien neu. Beginnen Sie bei 10.000

  • Ich musste eine Menge Daten importieren, also habe ich den Startwert auf 1 m erhöht, damit wir wissen, was importiert wird

  • Wir brauchen Kategorien von Daten. Jede Periode beginnt bei der nächsten Million, sodass wir die ersten Ziffern als magische Zahl verwenden können

  • Ich habe alle Daten gelöscht und erneut mit neuen IDs importiert. Ja, sogar die Audit-Protokolle.

  • Verwenden Sie diese Nummer, bei der es sich um einen zusammengesetzten Schlüssel handelt, als ID für diese andere Sache

Ewan
quelle
1
An dieser Antwort ist in der Tat nichts Falsches, aber ich möchte (um weitere Ablehnungen abzuwehren) vielleicht den Vorbehalt deutlich machen, dass es theoretisch möglich ist, dass bei realen Anwendungen keine Kollisionen auftreten. (Oder vielleicht sind mehr als 45 Exabyte-Datenbanken häufiger als ich dachte ...). Obwohl ich denke, dass die Sprache "der wichtigste Grund" ein bisschen stark ist, ist dies das, was ich am nützlichsten finde.
BurnsBA
2
Es ist wahrscheinlicher, dass ein Auto Inc Int kollidiert als ein Guid
Ewan
4
-1 für "Sie sollten in jeder Anwendung standardmäßig Guids verwenden." Es kommt darauf an ™. Und wie andere gezeigt haben, sind GUIDs / UUIDs absolut nicht eindeutig.
Max Vernon
3
"Es kommt darauf an" Antworten sind nutzlos, sicher gibt es einige seltsame Anwendungen, bei denen ein Int besser ist. Aber Ihre Bewerbung gehört wahrscheinlich nicht dazu. GUIDs sind die einzigartigste Sache, die Sie bekommen können
Ewan
2
Ich denke, es wird einige seltsame Anwendungen geben, bei denen die Anleitungen besser sind. Einzigartig ist nicht das Wichtigste. Ihre "Mängel" an Ints sind massiv übertrieben, und Sie berücksichtigen keine der vielen Nachteile von Guids.
Andy
2

Mir ist klar, dass diese GUIDs verwendet werden, um Objekte auf Anwendungsebene zu identifizieren. Werden sie auch als Primärschlüssel auf Datenbankebene gespeichert?

Hier sollten Sie genau anhalten und umdenken.

Der Primärschlüssel Ihrer Datenbank sollte NIEMALS geschäftliche Bedeutung haben. Es sollte per Definition bedeutungslos sein.

Fügen Sie daher die GUID als Geschäftsschlüssel und einen normalen Primärschlüssel (normalerweise einen langen int) als Datenbankprimärschlüssel hinzu. Sie können der GUID jederzeit einen eindeutigen Index zuweisen, um die Eindeutigkeit zu gewährleisten.

Das ist natürlich eine Diskussion über die Datenbanktheorie, aber es ist auch eine gute Praxis. Ich habe mich mit Datenbanken befasst, bei denen die Primärschlüssel geschäftliche Bedeutung hatten (ein Kunde hatte gedacht, einige Datenbankressourcen zu sparen, indem er sie beispielsweise als Mitarbeiternummern, Kundennummern usw. usw. verwendete), und dies führt immer zu Problemen.

jwenting
quelle
1
Inwiefern unterscheidet sich dies von Abfragen von der Anwendungsebene mithilfe eines ganzzahligen Primärschlüssels? An diesem Punkt wird es auch verwendet, um Objekte auf der Anwendungsebene zu identifizieren. Sie benötigen eine Möglichkeit, Objekte in einer Datenbank von der Anwendungsebene aus zu identifizieren.
Icirellik
@icirellik Der Primärschlüssel ist für die interne Verwendung durch die Datenbank vorgesehen, um übergeordnete und untergeordnete Datensätze und dergleichen zu verknüpfen. Es ist NICHT für die Verwendung durch die Anwendungslogik gedacht, Sie verwenden dafür Geschäfts-IDs, wie z. B. eine Produktnummer oder einen Produktnamen.
Mittwoch,
2

Verwenden Sie immer datenbankgenerierte, automatisch inkrementierende Primärschlüssel (PKs).

Warum Auto-Inkrementierung anstelle von GUID / UUID verwenden?

  • GUIDs (UUIDs) verhindern keine Schlüsselkollisionen, da sie nicht eindeutig sind und es keine Möglichkeit gibt, sie eindeutig zu machen, da sie aus zahlreichen Quellen generiert werden.
  • GUIDs helfen nicht beim Zusammenführen, da sie den ohnehin zeitaufwendigen Zusammenführungsprozess mit extrem langen, nicht ganzzahligen PK- und FK-Spalten, deren Verarbeitung viel Zeit in Anspruch nimmt, erheblich verlängern. Denken Sie daran, dass es für die meisten PKs mindestens einen weiteren Tisch mit mindestens zwei Schlüsseln derselben Größe gibt: einen eigenen PK und einen FK zurück zum ersten Tisch. Alle müssen in einem Merge aufgelöst werden.

Aber wie soll man dann mit Scherben, Clustern usw. umgehen?

  • Erstellen Sie mehrspaltige PKs, die aus separaten Spalten bestehen und die jeden Shard / Cluster / jede Datenbank identifizieren, der / die seine eigenen automatischen Inkrementierungsschlüssel verwaltet. Beispielsweise...

Ein 3-Spalten-PK für eine gruppierte Tabelle könnte sein ...

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

Aber was ist mit...?

  • Mehrere Fahrten in die Datenbank - Die meisten Anwendungen müssen einen Datensatz, der erstellt wird, erst eindeutig identifizieren, wenn er in die Datenbank eingefügt wurde, da dieser Thread / diese Sitzung / alles, was gerade bearbeitet wird, immer nur jeweils einzeln ausgeführt wird. Wenn die Anwendung diese Funktion wirklich benötigt, verwenden Sie eine von der Anwendung generierte temporäre PK , die nicht an die Datenbank gesendet wird . Lassen Sie die Datenbank ihre eigene PK mit automatischer Inkrementierung in die Zeile einfügen, wenn sie eingefügt wird. Einfügungen verwenden die temporäre PK, während Aktualisierungen und Löschungen die von der Datenbank zugewiesene permanente PK verwenden.

  • Leistung - Computer können einfache Ganzzahlen weitaus schneller verarbeiten als alles andere, da die Domäne nach Möglichkeit sehr viel größer ist und die Werte pro Element in einer GUID (37) im Vergleich zu einer Ganzzahl (10) stehen. Denken Sie auch daran, dass jedes Zeichen in einer GUID zuerst in eine Zahl umgewandelt werden muss, damit die CPU sie manipulieren kann.

Häufiger Missbrauch von Primärschlüsseln PKs haben nur einen Zweck ... eine Zeile in einer Tabelle absolut eindeutig zu identifizieren. Alles andere ist ein allzu häufiger Missbrauch.

Fehlende Datensätze erkennen

  • Fehlende Datensätze können nicht anhand der PKs erkannt werden. Segne die Qualitätssicherung, um zumindest die Datenqualität zu gewährleisten. Das Unverständnis der Benutzer und des Programmierers darüber, wie Schlüssel in modernen Datenbanksystemen zugewiesen werden, führt sie jedoch häufig zu dem Irrtum, dass eine fehlende Nummer in einer sich automatisch inkrementierenden PK zu fehlenden Daten führt. Das tut es nicht, weil ...
  • Um die Leistung zu verbessern, ordnen Datenbanksysteme Zahlenblöcke in "Sequenzen" (Chargen, Bereiche) zu, um die Anzahl der Fahrten zur eigentlichen Datenbank im Speicher zu minimieren. Die Größe dieser Zahlenfolgen unterliegt häufig der Kontrolle des Datenbankadministrators, ist jedoch möglicherweise nicht tabellenweise einstellbar.
  • Der Schlüssel zum Mitnehmen ist ... ungenutzte Nummern aus diesen Sequenzen werden niemals in die Datenbank zurückgesendet, daher gibt es immer Lücken in den PK-Nummern.
  • Warum gibt es nicht verwendete Nummern, die Sie fragen? Da verschiedene Datenbankpflegeaktionen dazu führen können, dass Sequenzen abgebrochen werden. Dies sind Dinge wie Neustarts, Massen-Neuladen von Tabellen, einige Arten der Wiederherstellung von Sicherungen und einige andere Vorgänge.

Sortierung

  • Die Sortierung nach PK ist sehr fehleranfällig, da die meisten Leute glauben, dass sie die Zeilen in der Reihenfolge auflistet, in der sie erstellt wurden und die der Uhrzeit entspricht. Meistens, aber nicht unbedingt.
  • Datenbank-Engines sind für maximale Leistung optimiert, und das kann bedeuten, dass das Einfügen der Ergebnisse einer lang andauernden komplizierten Transaktion verzögert wird, um kurze einfache, sozusagen "out-of-turn", einzufügen.
DocSalvager
quelle
Was halten Sie vom Tabellenschema, sodass die einzige eindeutige Spalte ein von der Datenbank erstellter, automatisch inkrementierender Primärschlüssel ist? Insbesondere für Tabellen, die keinen Fremdschlüssel haben, deren Primärschlüssel jedoch der Fremdschlüssel für mehrere verwandte Tabellen ist?
RibaldEddie
Ich habe der Antwort in diesem Sinne noch viel mehr hinzugefügt. Die ursprüngliche Antwort war unvollständig, da ich gerade mit der Android SE-App arbeite. Ich denke, eine umfassende Überarbeitung der App ist in Vorbereitung.
DocSalvager
Aus Ihrer Sicht wäre es also in Ordnung, wenn eine Tabelle eine beliebige Anzahl von Zeilen enthält, die identisch sind, abgesehen von ihrem automatisch inkrementierenden Primärschlüssel?
RibaldEddie
@RibaldEddie - Soweit es die DB zulässt ... absolut. Löschungen sind einfach. Wenn Ihr Szenario auftritt, würde ich es als Fehler ansehen, der in der Software behoben werden muss, und dann eine der Zeilen löschen. Der weitaus häufigere Fall sind jedoch zwei Datensätze für dasselbe Objekt mit geringfügig unterschiedlichen Daten, sodass sie zusammengeführt werden müssen. Wenn eine Spalte in einem Datensatz leer ist und in dem anderen einen Wert hat, ist die Auswahl offensichtlich und kann automatisiert werden. Oft kann der Datenzeitstempel verwendet werden, um eine automatisierte Zusammenführung zu vermitteln. Bei einigen Duplikaten muss eine Person die Zusammenführung basierend auf den Geschäftsregeln abschließen und überprüfen.
DocSalvager
1

Wie alles andere hat dies Vor- und Nachteile:

Das gute:

  1. Ihre Schlüssel sind immer gleich lang (sehr große Datenbanken können sehr große Schlüssel haben)

  2. Die Eindeutigkeit ist so gut wie garantiert - selbst wenn Sie sie aus einem separaten System generieren und / oder die letzte ID nicht aus der Datenbank gelesen haben

Das Schlechte:

  1. Wie oben schon viel erwähnt - größere Indizes und Datenspeicher.

  2. Sie können nicht nach Ausweis bestellen, sondern müssen nach etwas anderem bestellen. Mehr Indizes, wahrscheinlich weniger effizient.

  3. Sie sind weniger lesbar. Ganzzahlen sind im Allgemeinen einfacher zu analysieren, zu merken und für Menschen einzugeben. Die Verwendung von GUIDs als IDs in WHERE-Klauseln über mehrere verknüpfte Tabellen hinweg kann Ihren Kopf zum Schmelzen bringen.

Verwenden Sie sie, wie alles andere auch, und seien Sie nicht dogmatisch. In vielen Situationen sind automatisch inkrementierende Ganzzahlen besser, gelegentlich sind GUIDs großartig.

Phil S
quelle
0

Ja, Sie können GUID als Primärschlüssel verwenden. Der Nachteil ist die Größe und schnelle Fragmentierung des Index.

Sofern Sie keine datenbankübergreifende Eindeutigkeit benötigen (z. B. ein Cluster), wird eine Ganzzahl bevorzugt.

Paparazzo
quelle
GUID-Generatoren können dieselbe GUID mehr als einmal erzeugen, darin liegt ein Fehler. Ob sie es tun oder nicht, hängt von ihrer Granularität ab, hauptsächlich vom Intervall zwischen den Takten. Beispielsweise kann ein taktbasierter Generator nur alle 100 ms ankreuzen, was dazu führt, dass 2 GUIDs, die innerhalb dieser 100 ms auf dieser Maschine angefordert werden, identisch sind. Meistens gibt es Möglichkeiten, dies zu vermeiden, aber viele GUID-Generatoren arbeiten ausschließlich mit IP-Adressen und / oder MAC-Adressen und einem Zeitstempel.
Jwenting
0

Hier ist meine Sicht auf dieses Problem - die Lösung ist ein Mittelweg zwischen GUID- und int-Werten, wobei das Beste aus beiden Werten herausgearbeitet wird.

Die Klasse generiert einen Pseudozufalls-ID-Wert (der sich jedoch mit der Zeit erhöht), der einer Comb-GUID ähnelt .

Der Hauptvorteil besteht darin, dass ID-Werte auf dem Client generiert werden können, anstatt auf dem Server generierte Auto-Inkrement-Werte (für die ein Roundtrip erforderlich ist) zu verwenden, bei denen das Risiko doppelter Werte nahezu null ist.

Die generierten Werte verwenden nur 8 Bytes anstelle von 16 für eine GUID und sind nicht von einer bestimmten Sortierreihenfolge der Datenbank abhängig (z. B. SQL Server für GUIDs ). Die Werte könnten erweitert werden, um den gesamten vorzeichenlosen langen Bereich zu verwenden. Dies würde jedoch Probleme mit Datenbanken oder anderen Datenrepositorys verursachen, die nur ganzzahlige Typen mit Vorzeichen aufweisen.

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;
    }
}
Wanderer
quelle