Innerhalb einer Webanwendung, an der ich arbeite, werden alle Datenbankvorgänge mithilfe einiger über Entity Framework ORM definierter generischer Repositorys abstrahiert.
Um jedoch ein einfaches Design für die generischen Repositorys zu haben, müssen alle beteiligten Tabellen eine eindeutige Ganzzahl definieren ( Int32
in C #, int
in SQL). Bisher war dies immer der PK des Tisches und auch der IDENTITY
.
Fremdschlüssel werden häufig verwendet und verweisen auf diese ganzzahligen Spalten. Sie sind sowohl für die Konsistenz als auch für die Generierung von Navigationseigenschaften durch den ORM erforderlich.
Die Anwendungsschicht führt normalerweise die folgenden Vorgänge aus:
- Erstes Laden der Daten aus der Tabelle (*) -
SELECT * FROM table
- Update -
UPDATE table SET Col1 = Val1 WHERE Id = IdVal
- Löschen -
DELETE FROM table WHERE Id = IdVal
- Einfügen -
INSERT INTO table (cols) VALUES (...)
Weniger häufige Operationen:
- Masseneinfügung -
BULK INSERT ... into table
gefolgt von (*) allen Datenladevorgängen (um generierte Bezeichner abzurufen) - Massenlöschung - Dies ist eine normale Löschoperation, aus Sicht von ORM jedoch "voluminös":
DELETE FROM table where OtherThanIdCol = SomeValue
- Massenaktualisierung - Dies ist ein normaler Aktualisierungsvorgang, aus Sicht von ORM jedoch "umfangreich":
UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue
* Alle kleinen Tabellen werden auf Anwendungsebene zwischengespeichert und fast alle SELECTs
erreichen die Datenbank nicht. Ein typisches Muster ist die Anfangslast und viele INSERT
s, UPDATE
s und DELETE
s.
Aufgrund der aktuellen Anwendungsnutzung ist die Wahrscheinlichkeit sehr gering, dass jemals 100 Millionen Datensätze in einer der Tabellen erreicht werden.
Frage: Gibt es aus Sicht eines Datenbankadministrators erhebliche Probleme, die durch diese Einschränkung des Tabellenentwurfs auftreten können?
[BEARBEITEN]
Nach dem Lesen der Antworten (danke für das großartige Feedback) und der Artikel, auf die verwiesen wird, muss ich weitere Details hinzufügen:
Aktuelle Anwendungsspezifikationen - Ich habe die aktuelle Webanwendung nicht erwähnt, da ich verstehen möchte, ob das Modell auch für andere Anwendungen wiederverwendet werden kann. In meinem speziellen Fall handelt es sich jedoch um eine Anwendung, die viele Metadaten aus einem DWH extrahiert. Quelldaten sind ziemlich unübersichtlich (auf seltsame Weise denormalisiert, haben einige Inkonsistenzen, in vielen Fällen keine natürliche Kennung usw.) und meine App generiert klar getrennte Entitäten. Außerdem werden viele der generierten Bezeichner (
IDENTITY
) angezeigt, damit der Benutzer sie als Geschäftsschlüssel verwenden kann. Dies schließt neben einem massiven Code-Refactoring die Verwendung von GUIDs aus ."Sie sollten nicht die einzige Möglichkeit sein, eine Zeile eindeutig zu identifizieren" (Aaron Bertrand ♦) - das ist ein sehr guter Rat. Alle meine Tabellen definieren auch einen EINZIGARTIGEN KONSTRAINT, um sicherzustellen, dass Geschäftsduplikate nicht zulässig sind.
Front-End-App-gesteuertes Design im Vergleich zum datenbankgesteuerten Design - die Auswahl des Designs wird durch diese Faktoren verursacht
Entity Framework-Einschränkungen - PKs mit mehreren Spalten sind zulässig, ihre Werte können jedoch nicht aktualisiert werden
Benutzerdefinierte Einschränkungen : Durch die Verwendung eines einzelnen Ganzzahlschlüssels werden Datenstrukturen und Nicht-SQL-Code erheblich vereinfacht. Beispiel: Alle Wertelisten haben einen Integer-Schlüssel und einen angezeigten Wert. Noch wichtiger ist, dass jede zum Cachen markierte Tabelle in der Lage ist, eine
Unique int key -> value
Karte zu erstellen .
Komplexe Auswahlabfragen - dies wird so gut wie nie vorkommen, da alle kleinen Tabellendaten (<20-30 KB) auf Anwendungsebene zwischengespeichert werden. Dies macht das Leben ein wenig schwieriger beim Schreiben von Anwendungscode (schwieriger beim Schreiben von LINQ), aber die Datenbank wird viel besser getroffen:
Listenansichten - erzeugen
SELECT
beim Laden keine Abfragen (alles wird zwischengespeichert) oder Abfragen, die so aussehen:SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)
Alle anderen erforderlichen Werte werden über Cache-Lookups (O (1)) abgerufen, sodass keine komplexen Abfragen generiert werden.
Ansichten bearbeiten - erzeugt
SELECT
Anweisungen wie diese:SELECT allcolumns FROM BigTable WHERE PKId = value1
(alle Filter und Werte sind int
s)
Antworten:
Abgesehen von zusätzlichem Speicherplatz (und wiederum Speicherauslastung und E / A) kann das Hinzufügen einer IDENTITY-Spalte auch zu Tabellen, die keine IDENTITY-Spalte benötigen , nicht wirklich schaden (ein Beispiel für eine Tabelle, die keine IDENTITY-Spalte benötigt) ist eine einfache Junction-Tabelle (wie das Zuordnen eines Benutzers zu seinen Berechtigungen).
Ich bin dagegen, sie in einem Blogbeitrag aus dem Jahr 2010 blind jedem einzelnen Tisch hinzuzufügen :
Ersatzschlüssel haben jedoch gültige Anwendungsfälle. Achten Sie jedoch darauf, dass Sie nicht davon ausgehen, dass sie eindeutig sind (weshalb sie manchmal hinzugefügt werden - sie sollten nicht die einzige Möglichkeit sein, eine Zeile eindeutig zu identifizieren). Wenn Sie ein ORM-Framework verwenden müssen und für Ihr ORM-Framework einspaltige Ganzzahlschlüssel erforderlich sind, stellen Sie sicher, dass Sie eindeutige Einschränkungen / Indizes definieren, auch wenn es sich bei Ihrem tatsächlichen Schlüssel entweder nicht um eine Ganzzahl oder nicht um eine einzelne Spalte handelt auch für Ihre echten Schlüssel.
quelle
Nach meiner Erfahrung ist der Haupt- und Hauptgrund für die Verwendung einer separaten ID für jede Tabelle der folgende:
In fast allen Fällen hat mein Kunde in der Konzeptionsphase einen Blutschwur
XYZBLARGH_ID
geleistet, wonach ein externes "natürliches" Feld für immer einzigartig bleibt und sich für eine bestimmte Entität niemals ändert und niemals wiederverwendet wird Die Primärschlüsseleigenschaften waren fehlerhaft. So funktioniert es einfach nicht.Aus Sicht des Datenbankadministrators sind die Dinge, die eine Datenbank langsam oder aufgebläht machen, sicher nicht 4 Bytes (oder was auch immer) pro Zeile, sondern Dinge wie falsche oder fehlende Indizes, vergessene Tabellen- / Indexreorganisationen, falsche RAM- / Tabellenbereich-Optimierungsparameter , vernachlässigt die Verwendung von Bindevariablen und so weiter. Diese können den DB um den Faktor 10, 100, 10000 verlangsamen ... keine zusätzliche ID-Spalte.
Selbst wenn es einen technischen, messbaren Nachteil gäbe, zusätzliche 32 Bit pro Zeile zu haben, ist es keine Frage, ob Sie die ID wegoptimieren können, sondern ob die ID irgendwann unerlässlich sein wird, was mehr sein wird wahrscheinlich als nicht. Und ich werde nicht alle "weichen" Vorteile einer Softwareentwicklungshaltung abzählen (wie Ihr ORM-Beispiel oder die Tatsache, dass es für Softwareentwickler einfacher ist, wenn alle IDs von Entwurf den gleichen Datentyp haben und so weiter). .
Hinweis: Beachten Sie, dass Sie für
n:m
Zuordnungstabellen keine separate ID benötigen, da für diese Tabellen die IDs der zugeordneten Entitäten einen Primärschlüssel bilden sollten. Ein Gegenbeispiel wäre eine seltsamen:m
Assoziation, die aus irgendeinem bizarren Grund mehrere Assoziationen zwischen denselben beiden Entitäten zulässt. Diese benötigen dann ihre eigene ID-Spalte, um eine PK zu erstellen. Es gibt ORM-Bibliotheken, die jedoch nicht mit mehrspaltigen PKs umgehen können. Dies wäre ein Grund, umsichtig mit den Entwicklern umzugehen, wenn sie mit einer solchen Bibliothek arbeiten müssen.quelle
Wenn Sie ausnahmslos jeder Tabelle eine bedeutungslose zusätzliche Spalte hinzufügen und nur diese Spalten als Fremdschlüssel referenzieren, wird die Datenbank fast zwangsläufig komplexer und schwieriger zu verwenden sein. Tatsächlich entfernen Sie Daten, die für Benutzer von Interesse sind, aus den Fremdschlüsselattributen und zwingen den Benutzer / die Anwendung, einen zusätzlichen Join durchzuführen, um dieselben Informationen abzurufen. Abfragen werden komplexer, die Arbeit des Optimierers wird schwieriger und die Leistung kann leiden.
Ihre Tabellen werden spärlicher mit "echten" Daten gefüllt, als dies sonst der Fall gewesen wäre. Die Datenbank wird daher schwieriger zu verstehen und zu überprüfen sein. Möglicherweise ist es auch schwierig oder unmöglich, bestimmte nützliche Einschränkungen durchzusetzen (wobei für Einschränkungen mehrere Attribute erforderlich sind, die sich nicht mehr in derselben Tabelle befinden).
Ich würde vorschlagen, dass Sie Ihre Schlüssel sorgfältiger auswählen und sie nur dann ganzzahlig machen, wenn Sie gute Gründe dafür haben. Bauen Sie Ihre Datenbankdesigns auf eine gute Analyse, Datenintegrität, Praktikabilität und überprüfbare Ergebnisse, anstatt sich auf dogmatische Regeln zu verlassen.
quelle
Nach meiner Erfahrung mit verschiedenen Datenbanken ist ein Integer-Primärschlüssel immer besser als die Anwendungen, für die überhaupt keine Schlüssel definiert sind. Oder die Schlüssel haben, die ein halbes Dutzend varchar-Spalten auf unangenehme Weise verbinden, die nicht logisch sind ... (Seufzer)
Ich habe Anwendungen gesehen, die von ganzzahligen PKs auf GUIDs umgestellt haben. Der Grund dafür war, dass in bestimmten Fällen Daten aus mehreren Quellendatenbanken zusammengeführt werden mussten. Die Entwickler haben alle Schlüssel auf GUIDs umgestellt , sodass die Zusammenführung ohne Angst vor Datenkollisionen erfolgen kann, auch bei Tabellen, die nicht Teil der Zusammenführung waren (nur für den Fall, dass diese Tabellen jemals Teil einer zukünftigen Zusammenführung wurden).
Ich würde sagen, eine Ganzzahl-PK wird Sie nicht beißen, es sei denn, Sie planen, Daten aus unterschiedlichen Quellen zusammenzuführen, oder Sie haben Daten, die über Ihre Ganzzahl-Größengrenzen hinausgehen - es macht Spaß und ist ein Spiel, bis Ihnen der Platz für Einfügungen ausgeht .
Ich werde jedoch sagen, dass es sinnvoll sein kann , Ihren Clustered-Index für eine andere Spalte als Ihre PK festzulegen, wenn die Tabelle auf diese Weise häufiger abgefragt wird. Dies ist jedoch ein Ausnahmefall, insbesondere wenn der Großteil der Aktualisierungen und Auswahlen auf den PK-Werten basiert.
quelle
Zur Seite legen:
Vorausgesetzt, Sie verwenden Bulk-Lösch- / Aktualisierungsfunktionen und verfügen über Indizes, um solche Vorgänge zu unterstützen. Ich glaube nicht, dass Sie aufgrund des von Ihnen verwendeten PK-Standards auf Probleme stoßen werden.
Es ist möglich, dass EF-Abfragen mit Joins usw. später nicht so effizient sind wie mit einem natürlichen schlüsselbasierten Repository, aber ich weiß nicht genug über diesen Bereich, um es mit Sicherheit so oder so zu sagen.
quelle
Sie haben ein paar Faktoren, die Ihnen helfen,
Definition und Spezifikation
Wenn etwas durch die Aufgabe oder die Gesetze der Physik als einzigartig definiert ist, verschwenden Sie Ihre Zeit mit einem Ersatzschlüssel.
Einzigartigkeit.
Für die persönliche Vernunft, Verknüpfungen und Datenbankfunktionen auf höherer Ebene benötigen Sie entweder (a) eine eindeutige Spalte, (b) eine eindeutige Reihe von Spalten
Alle ausreichend normalisierten Schemata (1NF) bieten eine der folgenden Möglichkeiten. Wenn nicht, sollten Sie immer eine erstellen. Wenn Sie eine Liste von Personen haben, die sich am Sonntag freiwillig melden, und die den Nachnamen und den Vornamen enthält, möchten Sie wissen, wann Sie zwei Joe Bobs haben.
Implementierung und Optimierung.
Ein Int ist in der Regel eine kleine Datenform, die schnell zum Vergleich und zur Gleichheit dient. Vergleichen Sie dies mit einer Unicode-Zeichenfolge, deren Sortierung vom Gebietsschema (Standort und Sprache) abhängig sein kann. Das Speichern eines 4242 in einer ASCII / UTF8-Zeichenfolge umfasst 4 Byte. Speichern Sie es als Ganzzahl, es passt in 2 Bytes.
Wenn es also um Nachteile geht, gibt es ein paar Faktoren.
Verwirrung und Mehrdeutigkeit.
Platz.
Ganzzahlen fügen der Zeile noch Platz hinzu. Und wenn Sie sie nicht verwenden, gibt es keinen Zweck.
Clustering.
Sie können Ihre Daten nur in eine Richtung bestellen. Wenn Sie einen Ersatzschlüssel auferlegen, der nicht benötigt wird, gruppieren Sie diesen oder den natürlichen Schlüssel?
quelle