it-swarm.com.de

Wie generiere ich global eindeutige IDs für verschiedene Tabellen derselben Datenbank?

In einem Produktionssystem auf SQL Server werden alle IDs (meistens PKs) in allen Tabellen automatisch generiert und ich werde informiert, dass sie global eindeutig sind. Ich meine, keine 2 IDs sind in der Datenbank gleich, auch wenn die Tabellen unterschiedlich sind. Ich möchte wissen, wie das geht? Wenn es mehrere Möglichkeiten gibt, listen Sie bitte alle auf. Vielen Dank.

5
igelr

Früher hatten wir eine ID Tabelle. Einzelne Spalte, einzelne Zeile mit einem int -Wert. Bei jeder Transaktion wurde diese Tabelle zuerst aktualisiert, um einen neuen Wert zu erhalten, der dann überall dort verwendet wurde, wo er benötigt wurde. Dies war natürlich eine große Quelle für Parallelitätsfehler.

Später wurden Sequenzen eingeführt. Eine einzelne Sequenz, die in der gesamten Datenbank verwendet wird, zeigt das von Ihnen beschriebene Verhalten. Es gibt ein Beispiel in der Dokumentation, die dies veranschaulicht:

CREATE TABLE Audit.ProcessEvents
(
    EventID int DEFAULT (NEXT VALUE FOR Audit.EventCounter), -- same sequence, different table
    <other columns>
);

CREATE TABLE Audit.ErrorEvents
(
    EventID int DEFAULT (NEXT VALUE FOR Audit.EventCounter), -- same sequence, different tables
    <other columns>
);

Ich habe das Beispiel bearbeitet, um diese Verwendung hervorzuheben.

Ein identisches Ergebnis könnte erzielt werden, indem die global eindeutigen Nummern im Anwendungscode generiert werden, bevor sie an die Datenbank übergeben werden. Wenn ich dies implementieren würde, würde ich mir vorstellen, dass es sich um eine statische Methode einer in die ausführbare Datei kompilierten Dienstprogrammklasse handelt (obwohl andere Implementierungen möglich wären). Angenommen, die Anwendung muss die Kundendaten in die Datenbank schreiben. Beim Sammeln des Kundennamens, der Adresse, der Telefonnummer usw. wird auch eine neue globale ID generiert. Die ID wird als nur ein weiterer Parameterwert an die INSERT-Anweisung (oder gespeicherte Prozedur) übergeben.

Ob die ID-Werte von der Anwendungsarchitekturschicht oder der Datenbankebene erzeugt werden, hängt von den spezifischen Entwurfsüberlegungen ab. Wenn die App die Koordination zwischen Instanzen skalieren kann, wird dies problematisch. Nach einem Neustart einer Anwendung muss der Code den nächsten zu verwendenden Wert ermitteln. Auf dem DB-Server sind diese und andere Funktionen bereits geschrieben.

Was ich definitiv nicht tun würde , ist, dass die Anwendung die Datenbank nur für die nächste ID aufruft und diese dann mit den Geschäftsdaten in einem INSERT zusammenfasst. Das sind zu viele Roundtrips zur Datenbank, wenn nur einer erforderlich ist.

13
Michael Green

Für eindeutige ID-Werte in derselben Tabelle ist Ihnen vermutlich die häufig verwendete Option IDENTITY bekannt, die normalerweise einen 32-Bit-Wert ab 1 verwendet (um eine PK auf diese Weise wie ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY). Sie können natürlich ein größeres (BIGINT) verwenden, wenn die Tabelle mehr als 2.147.483.647 Zeilen benötigt.

SQL Server bietet die Möglichkeit, eine eigene Sequenz zu definieren, die von mehreren Tabellen, möglicherweise allen, gemeinsam genutzt werden kann. Weitere Informationen finden Sie unter https://docs.Microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql . Anschließend definieren Sie jede ID-Spalte als ID INTEGER DEFAULT NEXT VALUE FOR The_sequence_You_Defined PRIMARY KEY. Hier sind jedoch einige Dinge zu beachten. Anders als bei IDENTITY können Sie keinen alten Wert (der noch nicht vorhanden ist) einfügen, da der Sequenzwert standardmäßig nur angewendet wird, wenn einer nicht explizit angegeben wird, was problematisch sein kann. Die Verwendung einer Sequenz wird etwas langsamer ausgeführt und kann zu einem Engpass werden, da alle Tabellen auf demselben Objekt basieren. Beide Probleme sind jedoch nur dann von Bedeutung, wenn Ihre Datenbank in kurzer Zeit viele Einfügetätigkeiten aufweist. NEXT VALUE FOR The_sequence_You_Defined Kann auch an anderer Stelle verwendet werden (dh SET @someVariable = NEXT VALUE FOR The_sequence_You_Defined;). Wenn Sie also IDs benötigen, die an anderer Stelle in Ihrer Anwendungslogik generiert werden sollen, können Sie dies auf diese Weise tun (tatsächlich habe ich das gesehen wird auch für eine einzelne Identität verwendet (nicht nur für die gemeinsame Nutzung einer Sequenz zwischen mehreren Objekten).

Ein hackigerer Ansatz könnte darin bestehen, für jede Identitätsspalte ein BIGINT zu verwenden und jedes mit einem anderen Vielfachen von (zum Beispiel) 4.000.000.000 zu beginnen. Dies funktioniert in anderen DBs und vermeidet das Engpassproblem, verdoppelt jedoch die Größe Ihres Schlüssels und kann zu einem Wartungsalptraum führen, wenn Sie versehentlich zwei Tabellen mit IDs definieren, die an derselben Stelle beginnen. Möglicherweise möchten Sie Prüfeinschränkungen hinzufügen, um sicherzustellen, dass ein auf diese Weise definierter Identitätswert nicht in den Nummernraum eines anderen Werts überläuft, was zu Leistungsproblemen führt.

Wenn Ihnen der größere Schlüssel nichts ausmacht, sind UUID s nützlich und haben den zusätzlichen Vorteil, dass sie zwischen Datenbanken eindeutig sind (alle Datenbanken, wie der Name schon sagt) ) nicht nur zwischen Tabellen in einer Datenbank. Wie bei einer Sequenz werden diese mit einer Standardeinschränkung angewendet, d. H. ID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT NEWID(). Dies sind jedoch 128-Bit-Werte, doppelt so groß wie BITINT und viermal so groß wie ein "Standard" 32-Bit INTEGER. Wenn Sie sich Sorgen über die Möglichkeit einer zusätzlichen Fragmentierung machen, die durch die Zufälligkeit von v4-UUIDs verursacht wird, können Sie NEWSEQUENTIALID() anstelle von NEWID() verwenden, die immer noch eindeutig genug sein sollte (die Wahrscheinlichkeit einer Kollision in der Die Lebensdauer dieser Galaxie ist verschwindend gering.

7
David Spillett

Zunächst muss ich erwähnen, dass ich nicht mit SQL Server gearbeitet habe, sodass ich auf einige spezifische Funktionen nicht hinweisen kann.

Ich habe zwei Konzepte, wie dies in meinem Kopf getan werden kann:

  1. Eine Sequenz, um sie alle zu regieren: Dieses Konzept ist so einfach, wie es sich anhört. Sie haben eine Sequenz, die für die Generierung von IDs für jede in eine Tabelle eingefügte Zeile verantwortlich ist. In meinem letzten Job haben wir dieses Konzept verwendet. Die Implementierung hängt von vielen Umständen ab, daher werde ich Sie entscheiden lassen. Eine Möglichkeit besteht jedoch darin, eine gespeicherte Prozedur zu haben, die den nächsten Wert der Sequenz vor jeder Einfügung abruft.
  2. Zeitstempel: Sie können Zeitstempel irgendwie in Ihre IDs integrieren

In der SQL Server-Welt können Sie auf Folgendes verweisen: NEWID () -Dokumentation - newid ist kompatibel mit RFC4122