it-swarm.com.de

Was kann bei Verwendung derselben Sequenz über mehrere Tabellen in Postgres hinweg schief gehen?

Wir erwägen, eine gemeinsame Sequenz zu verwenden, um Primärschlüsseln für alle Tabellen in unserer Datenbank IDs zuzuweisen. Es gibt ungefähr 100 von ihnen. Nur ein paar werden häufig und regelmäßig eingefügt. Wir möchten ausschließen, dass es "aus einem offensichtlichen Grund eine schreckliche Idee" ist, bevor wir uns der Phase des tatsächlichen Versuchs und Testens unter Last zuwenden.

Unsere Spitzenlast liegt in der Größenordnung von 1000 Einsätzen pro Sekunde über mehrere Tabellen hinweg.

Unsere bisherigen Untersuchungen zeigen, dass - Sequenzgenerierungsgeschwindigkeit kein Problem sein sollte - Sequenzfragmentierung (Lücken) auftreten wird, aber kein Problem sein sollte - ID-Erschöpfung kein Problem sein wird

Wir sind uns nicht sicher, ob wir andere große Dinge vermissen. Wir wären dankbar für die Meinungen der Menschen, insbesondere von Menschen, die es zuvor versucht haben und entweder positive oder negative Erfahrungen gemacht haben.

Für den Kontext haben wir zwei Hauptmotive dafür.

Eine Motivation dafür ist, dass wir eine Reihe von Wörterbüchern definieren können (wir nennen sie Bereiche) und diesen IDs von Menschen lesbare Wörter zugewiesen bekommen. Daher möchten wir sicherstellen, dass sich IDs in verschiedenen Tabellen niemals überschneiden. In einem Bereich kann der ID 12345 der Wert "Grün" und in einem anderen Bereich "Verde" zugewiesen werden. (Eigentlich verwenden wir es nicht für die Internationalisierung, aber wir könnten eines Tages).

Die andere Motivation besteht darin, es einfach zu machen, mehrere Bereitstellungen vor Ort zu haben und zu wissen (indem Sie die Reihenfolge der wichtigsten Ziffern jeder Bereitstellung eindeutig festlegen), dass sich unsere Bereitstellungen nicht mit Primärschlüsseln überschneiden. (Wie ein GUID lite).

10
Burleigh Bear

Drei mögliche Probleme, die mir in den Sinn kommen, sind:

  1. Mit jeder gemeinsam genutzten Ressource schaffen Sie einen potenziellen Engpass. Mein Bauch sagt, dass dies für Ihre Spitzenlast kein Problem sein sollte, aber ich empfehle dringend, eine solche Lösung in einer produktionsähnlichen Produktionsgröße zu vergleichen, um sicherzugehen.

  2. Sie weisen Ersatzschlüsseln im Wesentlichen eine Bedeutung zu, die einen Teil ihres Zwecks in der RDB-Theorie zunichte macht. Ein Ersatzschlüssel sollte von Natur aus keine Bedeutung haben, die über die Identifizierung von Tupeln in dieser Beziehung hinausgeht. Wenn die Entitäten zusammen eine Bedeutung haben und daher kollisionsfreie Schlüssel benötigen, ist es richtig, dass sie separat modelliert werden, oder wurde etwas in den Anforderungen und/oder im Datenmodelldesign übersehen?

  3. Sie führen einen potenziellen Fehlerpunkt ein. Was ist, wenn für eine Bereitstellung der Startpunkt für die anfängliche Sequenz nicht festgelegt wird? Sie haben dann entweder einen Fehler beim Blockieren der Bereitstellung oder die Bereitstellung beginnt an derselben Stelle, an der Ihre Funktion "beschädigt" wird. Was werden Sie auch tun, wenn irgendwann jemand der Meinung ist, dass es eine gute Idee ist, eine Bereitstellung zu verzweigen (in der Produktion veräußert möglicherweise ein Mandantenunternehmen einen Teil von sich selbst und muss die Daten trennen). Was ist, wenn der Startwert durch eine fehlerhafte Upgrade-Bereitstellung oder eine andere Migration zurückgesetzt wird?[0]

Wenn Sie keines dieser Probleme betrifft, wird die Idee IMO nicht zerstören. Natürlich kann es bessere Wege geben, auch wenn dieser an sich nicht falsch ist.


Wenn Sie "UUID-lite" sagen, implizieren Sie, dass Sie UUIDs bereits berücksichtigt und abgezinst haben. Ist das der Fall und wenn ja, gibt es bestimmte Gründe für die Entscheidung, dass sie für dieses Projekt nicht geeignet sind?

Ein möglicher Grund für die Nichtverwendung von UUIDs ist die Indexfragmentierung, obwohl deren Bedeutung häufig stark überbewertet wird[1]. Die Antwort von SQL Server darauf ist die "sequentielle GUID", die ziemlich genau dem entspricht, was Sie vorschlagen, wenn wir die Zuweisung von Bedeutung zu Schlüsselwerten nicht berücksichtigen - vielleicht hat postgres eine Entsprechung dazu? Natürlich können immer größere Indizes ihre eigenen Leistungsprobleme haben (Konflikte auf der letzten Seite, Indexstatistiken werden immer veralteter), und zwar bei einigen sehr spezifischen Workloads mit hohem Volumen[2].

Ein weiteres häufiges Argument gegen UUIDs ist die Schlüssellänge: Warum 16 Bytes pro Wert verwenden, wenn 4 oder 8 ausreichen? Wenn die Einzigartigkeit wirklich eine nützliche Eigenschaft ist, wird dies in der Regel die Bedenken hinsichtlich der Schlüsselgröße erheblich übertreffen. Wenn die Schlüsselgröße ein Problem darstellt, Sie jedoch gerne eine 64-Bit-INT verwenden, anstatt innerhalb von 32-Bit zu bleiben, können Sie Ihre Technik verwenden, ohne ein potenzielles Problem mit Konflikten mit gemeinsam genutzten Ressourcen hinzuzufügen, indem Sie Ihre Idee für einen gesetzten Ganzzahlschlüssel ausführen pro Tisch[3] mit einem normalen INT IDENTITY(<start>, 1)[4] Spaltendefinition, obwohl dies wiederum die Komplexität der Bereitstellung erhöht (eine kleine Menge, aber sicherlich nicht Null).

Die menschliche Lesbarkeit wird manchmal als Problem angeführt, aber das geht zurück auf die Zuweisung von Bedeutung zu Ersatzschlüsseln.

Komprimierbarkeit ist ein weniger verbreitetes Problem, auf das Sie jedoch möglicherweise stoßen. Für nahezu jeden Komprimierungsalgorithmus sehen UUIDs wahrscheinlich wie zufällige (daher nicht komprimierbare) Daten aus, es sei denn, Sie verwenden so etwas wie die sequentiellen UUIDs von SQL Server. Dies kann ein Problem für eine sehr große Anzahl von Links (oder anderen Datenblöcken) sein, die viele Entitäts-IDs enthalten, die einer Anwendung über ein langsames Netzwerk bereitgestellt werden oder wenn Sie so etwas wie die Indexkomprimierungsfunktionen von SQL Server verwenden müssen, obwohl beide Aspekte im Wesentlichen nur das Problem der Schlüsselgröße auf eine etwas andere Art und Weise neu formulieren und sequentielle UUIDs auch hier hilfreich sein können.


[0] Dies könnte natürlich auch für normale Identitätsspalten passieren, aber da Sie eine weniger verbreitete Funktion verwenden, erhöhen Sie die Wahrscheinlichkeit eines weniger erfahrenen DBA, nachdem Sie das Problem verpasst haben, wenn es passiert, wenn Sie etwas Neues und Aufregendes tun anderswo!

[1] Ich bin ein SQL Server-Typ. Ich vermute, dass das potenzielle Problem bei Postgres dasselbe ist, aber soweit ich weiß, hat es möglicherweise ein anderes Indexlayout, das den Effekt abschwächen kann.

[2] Auch hier kann es sich um SQL Server-spezifisch handeln, insbesondere um das letztere der beiden von mir aufgelisteten Beispiele

[3] Die ersten beiden Bytes: variieren je nach Datenbank, die nächsten beiden: variieren je nach Tabelle, die restlichen vier: die inkrementierenden Bits

[4] Das ist die MS SQL Server-Syntax. Die Postgres-Syntax kann variieren, aber Sie sollten sehen, was ich meine, und in der Lage sein, zu übersetzen


tl; dr : Wenn Sie das Rad neu erfinden, stellen Sie sicher, dass alle vorhandenen Designs wirklich nicht geeignet sind bevor Sie anfangen zu überlegen, warum ein neuer sein könnte oder nicht.

5
David Spillett

Wir erwägen, eine gemeinsame Sequenz zu verwenden, um Primärschlüsseln für alle Tabellen in unserer Datenbank IDs zuzuweisen. Es gibt ungefähr 100 von ihnen. Nur ein paar werden häufig und regelmäßig eingefügt. Wir möchten ausschließen, dass es "aus einem offensichtlichen Grund eine schreckliche Idee" ist, bevor wir uns der Phase zuwenden, in der wir es tatsächlich ausprobieren und unter Last testen.

Das ist eine schreckliche Idee: Ausschluss. Verwenden Sie einfach eine GUID/UUID. Warum haben Sie diese Idee ausgeschlossen? In PostgreSQL verwenden wir uuid-ossp ,

uuid_generate_v4() Diese Funktion generiert eine UUID der Version 4, die vollständig aus Zufallszahlen abgeleitet wird.

So was,

CREATE EXTENSION uuid-ossp;
CREATE TABLE f ( f_id uuid DEFAULT uuid_generate_v4() );

Sie machen in Ihrer Antwort viele Annahmen, damit sie gültig ist.

  • geschwindigkeit "sollte kein Problem sein"
  • lücken "sollten kein Problem sein"
  • iD Erschöpfung wird nicht passieren

Sie müssen nichts davon annehmen. Was ist, wenn Sie ein DOS auf der ID erhalten, das eine massive Lücke erzeugt und einen Rollover auf einen Shard drückt? Warum nicht einfach die Branchenlösung für dieses Problem verwenden? Es ist nicht klar, dass es einen einzigen Nachteil gibt. Es ist wahrscheinlich, dass alle gewinnen. Bis auf ein paar Bytes Speicher.

3
Evan Carroll

Ich habe das von Ihnen vorgeschlagene Muster mit einer zusätzlichen zentralen ID-Tabelle verwendet, für die alle anderen IDs Fremdschlüssel sind. Es funktionierte in einem großen Produktionssystem völlig in Ordnung.

Ich denke, der wahre Grund dafür ist, dass Ihre IDs einen Bereich haben, der über Ihre Datenbank hinausgeht. In meinem Beispiel wurden in diesen IDs beispielsweise eindeutige finanzielle Wertpapiere und Unternehmen aufgeführt. Sie könnten sich fragen, warum Sie nicht einen Satz if-IDs für Unternehmen und einen zweiten Satz für Wertpapiere als Primärschlüssel für die automatische Zuordnung für jede Tabelle erstellen sollten. Weil wir wollten, dass sich andere Zeitreihenaufzeichnungen entweder auf Wertpapiere oder auf Unternehmen beziehen. Die Zeitreihentabelle ist also fremd mit der zentralen ID-Tabelle verknüpft.

In Anbetracht des oben Gesagten würde eine GUID/UUID ebenfalls gut funktionieren. Diese Formate sind jedoch häufig 128 Bit groß, was sich auswirken kann, da sie in fast allen Indizes, Primärschlüsseln und Fremdkörpern verwendet werden Das Eingeben der Datenbank und das Verringern der nicht sequentiellen Platzierung im gesamten ID-Bereich kann schwierig sein und zu einer nicht optimalen Auswahlleistung führen. Unsere Datenbank war sehr darauf ausgerichtet, die Leistung auszuwählen.

GUIDs/UUIDs haben einen Vorteil: Sie lassen sich viel einfacher mit Verbundgenerierungsprozessen erstellen. Das heißt, Sie können mehrere ID-Generierungs-/Zuweisungsprozesse in Ihrem Unternehmen ohne Koordination durchführen, indem Sie einfach davon ausgehen, dass sie niemals in Konflikt geraten. Wenn sich Ihre einzigen ID-Generierungsprozesse in Ihrer Datenbank befinden, ist dies weniger bedenklich, aber erwähnenswert.

Beachten Sie, dass die UUID-Generierung davon abhängt, dass Ihre MAC-Adressen eindeutig sind. Daher müssen Sie dies in einer virtuellen/Container-Umgebung berücksichtigen.

0
ThatDataGuy

Eine Motivation dafür ist, dass wir eine Reihe von Wörterbüchern definieren können (wir nennen sie Bereiche) und diesen IDs von Menschen lesbare Wörter zugewiesen bekommen. Daher möchten wir sicherstellen, dass sich IDs in verschiedenen Tabellen niemals überschneiden. In einem Bereich kann der ID 12345 der Wert "Grün" und in einem anderen Bereich "Verde" zugewiesen werden. (Eigentlich verwenden wir es nicht für die Internationalisierung, aber wir könnten eines Tages).

Allein würde ich nicht zulassen, dass dies der Grund für die Wahl eines skurrilen und fragilen Designs ist. Wenn Sie den Weg gehen, gibt es keine Möglichkeit, die Datenbankfunktionen zu nutzen, um beispielsweise die referenzielle Integrität sicherzustellen. Ein traditioneller normalisierter Weg, dasselbe zu erreichen, hätte Vorteile, die über RI hinausgehen:

create table tab1(tab1_id serial primary key);
create table tab2(tab2_id serial primary key);
create table scope(scope_id serial primary key, scope_name text);
create table scope_tab1(scope_id integer references scope, tab1_id integer references tab1, val text, primary key(scope_id,tab1_id));
insert into scope(scope_name) values ('English'),('French');
insert into tab1(tab1_id) select generate_series(1,5);
insert into tab2(tab2_id) select generate_series(1,5);
insert into scope_tab1(scope_id,tab1_id,val) values (1,1,'Green'),(2,1,'Verde');
select tab1_id
     , (select val from scope_tab1 where scope_id=1 and tab1_id=tab1.tab1_id) val_s1
     , (select val from scope_tab1 where scope_id=2 and tab1_id=tab1.tab1_id) val_s2
from tab1;
 tab1_id | val_s1 | val_s2 
 ------: | : -- : ----- 
 1 | Grün | Verde 
 2 |  null  |  null  
 3 |  null  |  null  
 4 |  null  |  null  
 5 |  null  |  null 

dbfiddle --- (hier

Die andere Motivation besteht darin, es einfach zu machen, mehrere Bereitstellungen vor Ort zu haben und zu wissen (indem Sie die Reihenfolge der wichtigsten Ziffern jeder Bereitstellung eindeutig festlegen), dass sich unsere Bereitstellungen nicht mit Primärschlüsseln überschneiden. (Wie ein GUID lite).

Ich würde vorschlagen, wie andere es getan haben, dass die Verwendung von UUID viel besser (dh viel weniger fehleranfällig) ist als die Erfindung einer neuen UUID-Lite.

Ich denke immer noch nicht, dass dies die beste Wahl ist - Sie sind nicht am Splittern, sodass zwischen den Bereitstellungen keine nicht überlappenden IDs erforderlich sind, die ich anhand der von Ihnen bereitgestellten Informationen sehen kann. Vermutlich haben Sie andere Möglichkeiten, eine Bereitstellung in einer Datenbank zu identifizieren, als die IDs in diesen Tabellen zu betrachten.