Wir arbeiten an einer Webanwendung, auf die Benutzer noch keinen Zugriff haben. Mein Chef bemerkte, dass neu erstellte Datensätze eine ID von über 10 000 erhalten, obwohl wir nur weniger als 100 Datensätze in der Tabelle haben. Sie ging davon aus, dass das Webinterface aus irgendeinem Grund mehr als 100-mal mehr temporäre Datensätze erstellt (und löscht), was dazu führen kann, dass wir innerhalb weniger Monate nach der Veröffentlichung nicht mehr in Reichweite sind.
Ich glaube nicht, dass sie bezüglich der Ursache der ID-Inflation Recht hat (die Kollegin, die darauf antworten kann, ist im Urlaub, wir wissen es also nicht genau), aber nehmen wir an, dass sie es ist. Sie sagte, sie würde es hassen, eine Bigint-Spalte zu verwenden, und sie möchte, dass wir die automatische Inkrementierung der ID-Spalte beenden und serverseitigen Code schreiben, der die erste "unbenutzte" Ganzzahl auswählt und sie als ID verwendet.
Ich bin ein Student der Informatik mit wenig praktischer Erfahrung und übernehme eine Junior-Entwickler-Rolle. Sie verfügt über jahrelange Erfahrung in der Verwaltung aller Datenbanken unserer Organisation und im Entwurf der meisten Datenbanken. Ich denke, dass sie in diesem Fall falsch ist, dass eine Bigint-ID nichts zu befürchten ist und dass die Nachahmung der DBMS-Funktionalität nach einem Antipattern riecht. Aber ich traue meinem Urteil noch nicht.
Was sind die Argumente für und gegen jede Position? Welche schlechten Dinge können passieren, wenn wir einen Bigint verwenden, und welche Gefahren birgt die Neuerfindung der Funktion zum automatischen Inkrementieren des Rads ? Gibt es eine dritte Lösung, die besser ist als beide? Was könnte ihre Gründe dafür sein, eine Inflation von ID-Nennwerten vermeiden zu wollen? Ich bin auch daran interessiert, über pragmatische Gründe zu hören - vielleicht funktionieren Bigint-IDs theoretisch, verursachen aber in der Praxis Kopfschmerzen?
Es wird nicht erwartet, dass die Anwendung sehr große Datenmengen verarbeitet. Ich bezweifle, dass es in den nächsten Jahren 10 000 tatsächliche Rekorde erreichen wird.
Wenn es einen Unterschied macht, verwenden wir Microsoft SQL Server. Die Anwendung ist in C # geschrieben und verwendet Linq to SQL.
Aktualisieren
Vielen Dank, ich fand die vorhandenen Antworten und Kommentare interessant. Aber ich fürchte, Sie haben meine Frage falsch verstanden, und sie enthalten das, was ich wissen wollte.
Ich bin nicht wirklich besorgt über den wahren Grund für die hohen IDs. Wenn wir es nicht alleine finden können, könnte ich eine andere Frage stellen. Was mich interessiert, ist, den Entscheidungsprozess in diesem Fall zu verstehen. Nehmen Sie dazu bitte an, dass die Anwendung 1000 Datensätze pro Tag schreibt und 9999 davon löscht . Ich bin mir fast sicher, dass dies nicht der Fall ist, aber das hat meine Chefin geglaubt, als sie ihre Anfrage gestellt hat. Was wäre also unter diesen hypothetischen Umständen das Für und Wider, entweder Bigint zu verwenden oder unseren eigenen Code zu schreiben, der IDs zuweist (auf eine Weise, die die IDs bereits gelöschter Datensätze wiederverwendet, um sicherzustellen, dass es keine Lücken gibt)?
Was den eigentlichen Grund betrifft, vermute ich stark, dass dies daran liegt, dass wir einmal Code geschrieben haben, um Daten aus einer anderen Datenbank zu importieren, um zu beweisen, dass eine spätere Migration bis zu einem gewissen Grad durchgeführt werden kann. Ich glaube, mein Kollege hat während des Imports tatsächlich mehrere tausend Datensätze erstellt und später gelöscht. Ich muss bestätigen, ob dies tatsächlich der Fall war, aber wenn ja, besteht nicht einmal Handlungsbedarf.
quelle
Antworten:
Ohne den Code zu sehen, ist es ziemlich schwer abschließend zu sagen, was passiert. Wahrscheinlich wird der
IDENTITY
Wert jedoch zwischengespeichert, was nach dem Neustart von SQL Server zu Lücken im Wert führt. Unter /programming/17587094/identity-column-value-suddenly-jumps-to-1001-in-sql-server finden Sie einige gute Antworten und Informationen dazu.Ein einfaches
INT
Feld kann Werte bis zu 2.147.483.647 enthalten. Sie können den Identitätswert tatsächlich bei -2.147.483.648 beginnen und dabei 32 Bit mit Werten angeben. 4 Milliarden verschiedene Werte. Ich bezweifle sehr, dass Ihnen die Werte ausgehen, die Sie verwenden können. Angenommen , Ihre Anwendung wird 1000 Werte für jede tatsächliche Zeilenraubend hinzugefügt, dann würden Sie brauchen fast 12.000 Zeilen pro Tag zu erschaffen täglich von IDs laufen in 6 Monaten vorausgesetzt , Sie den gestartetIDENTITY
Wert bei 0, und ein INT verwendet hat . Wenn Sie einen BIGINT verwenden, müssen Sie 21 Millionen Jahrhunderte warten, bis Ihnen die Werte ausgehen, wenn Sie 12.000 Zeilen pro Tag schreiben und 1.000 "Werte" pro Zeile verbrauchen.Abgesehen davon, wenn Sie
BIGINT
als Datentyp für das Identitätsfeld verwenden möchten, ist daran sicherlich nichts auszusetzen. Das gibt Ihnen in jeder Hinsicht ein unbegrenztes Angebot an Werten, die Sie nutzen können. Der Leistungsunterschied zwischen einem INT und einem BIGINT ist auf moderner 64-Bit-Hardware praktisch nicht vorhanden und wird beispielsweise bei der Erstellung vonNEWID()
GUIDs vorgezogen.Wenn Sie Ihre eigenen Werte für die ID-Spalte verwalten möchten, können Sie eine Schlüsseltabelle erstellen und dies auf ziemlich sichere Weise tun, indem Sie eine der in den Antworten zu dieser Frage aufgeführten Methoden verwenden: Gleichzeitigen Zugriff auf eine Schlüsseltabelle ohne Deadlocks in SQL Server
Wenn Sie SQL Server 2012+ verwenden, können Sie auch ein
SEQUENCE
Objekt verwenden, um ID-Werte für die Spalte abzurufen . Sie müssen die Sequenz jedoch so konfigurieren, dass keine Werte zwischengespeichert werden. Beispielsweise:Als Antwort auf die negative Wahrnehmung Ihres Chefs von "hohen" Zahlen würde ich sagen, welchen Unterschied macht es? Vorausgesetzt , dass Sie ein verwenden
INT
Feld, mit einIDENTITY
, könnte man in der Tat beginnt dieIDENTITY
an2147483647
und „Erhöhung“ der Wert durch-1
. Dies würde absolut keinen Unterschied hinsichtlich des Speicherverbrauchs, der Leistung oder des verwendeten Speicherplatzes machen, da eine 32-Bit-Zahl 4 Bytes beträgt, unabhängig davon, ob dies der Fall ist0
oder nicht2147483647
.0
In binär ist,00000000000000000000000000000000
wenn in einem 32-Bit-INT
Feld mit Vorzeichen gespeichert .2147483647
ist01111111111111111111111111111111
- Beide Nummern belegen sowohl im Arbeitsspeicher als auch auf der Festplatte genau den gleichen Speicherplatz und erfordern für die Verarbeitung genau die gleiche Anzahl von CPU-Vorgängen. Es ist weitaus wichtiger, Ihren Anwendungscode korrekt zu gestalten, als sich über die in einem Schlüsselfeld gespeicherte tatsächliche Nummer Gedanken zu machen.Sie haben nach den Vor- und Nachteilen gefragt, entweder (a) eine ID-Spalte mit größerer Kapazität wie a zu verwenden
BIGINT
oder (b) eine eigene Lösung zu entwickeln, um ID-Lücken zu vermeiden. Um diese Bedenken zu beantworten:BIGINT
stattINT
als Datentyp für die betreffende Spalte. Die Verwendung von aBIGINT
erfordert die doppelte Menge an Speicher sowohl auf der Festplatte als auch im Arbeitsspeicher für die Spalte selbst. Wenn die Spalte der Primärschlüsselindex für die betreffende Tabelle ist, speichert jeder nicht gruppierte Index, der an die Tabelle angehängt ist, denBIGINT
Wert ebenfalls mit der doppelten Größe einesINT
, ebenfalls sowohl im Arbeitsspeicher als auch auf der Festplatte. SQL Server speichert Daten auf der Festplatte in 8-KB-Seiten, wobei die Anzahl der "Zeilen" pro "Seite" von der "Breite" jeder Zeile abhängt. Wenn Sie beispielsweise eine Tabelle mit 10 Spalten haben, von denen jede eine istINT
, können Sie ungefähr 160 Zeilen pro Seite speichern. Wenn diese Spalten stattdessen warenBIGINT
In Spalten können Sie nur 80 Zeilen pro Seite speichern. Bei einer Tabelle mit einer sehr großen Anzahl von Zeilen bedeutet dies eindeutig, dass die zum Lesen und Schreiben der Tabelle erforderlichen E / A-Vorgänge in diesem Beispiel für eine bestimmte Anzahl von Zeilen doppelt so hoch sind. Zugegeben, dies ist ein ziemlich extremes Beispiel - wenn Sie eine Zeile bestehend aus einer einzelnenINT
oderBIGINT
Spalte und einer einzelnenNCHAR(4000)
Spalte hätten, würden Sie (vereinfacht) eine einzelne Zeile pro Seite erhalten, unabhängig davon, ob Sie eineINT
oder eine verwendet habenBIGINT
. In diesem Szenario würde es keinen nennenswerten Unterschied machen.Rollen Sie Ihr eigenes Szenario, um Lücken in der ID-Spalte zu vermeiden. Sie müssen Ihren Code so schreiben, dass die Bestimmung des "nächsten" zu verwendenden ID-Werts nicht mit anderen Aktionen in Konflikt steht, die mit der Tabelle durchgeführt werden. Etwas in der Art von
SELECT TOP(1) [ID] FROM [schema].[table]
Naivität fällt mir ein. Was ist, wenn mehrere Akteure gleichzeitig versuchen, neue Zeilen in die Tabelle zu schreiben? Zwei Akteure könnten leicht den gleichen Wert erhalten, was zu einem Schreibkonflikt führen würde. Um dieses Problem zu umgehen, muss der Zugriff auf die Tabelle serialisiert werden, wodurch die Leistung verringert wird. Es wurden viele Artikel über dieses Problem geschrieben. Ich überlasse es dem Leser, nach diesem Thema zu suchen.Die Schlussfolgerung hier ist: Sie müssen Ihre Anforderungen verstehen und sowohl die Anzahl der Zeilen als auch die Zeilenbreite sowie die Anforderungen an die Parallelität Ihrer Anwendung richtig einschätzen. Wie gewohnt ist It Depends ™.
quelle
bigint
Sie sich wahrscheinlich dafür bedanken, dass Sie dies im Voraus entschieden haben, anstatt dies einer Tabelle mit Milliarden von Zeilen hinzufügen zu müssen.Die Hauptaufgabe besteht darin, die Ursache zu finden, warum der aktuelle Wert so hoch ist.
Die vernünftigste Erklärung für SQL Server-Versionen vor SQL2012 - vorausgesetzt, Sie sprechen von einer Testdatenbank - wäre, dass ein Auslastungstest gefolgt von einer Bereinigung durchgeführt wurde.
Ab SQL2012 liegt der wahrscheinlichste Grund in mehreren Neustarts der SQL-Engine (wie im ersten angegebenen Link Max erläutert).
Wenn die Lücke durch ein Testszenario verursacht wird, besteht aus meiner Sicht kein Grund zur Sorge. Aber um auf der sicheren Seite zu sein, würde ich die Identitätswerte während der normalen Verwendung der Anwendung sowie vor und nach einem Motorneustart überprüfen.
Es ist "lustig", dass MS angibt, dass beide Alternativen (entweder Ablaufverfolgungsflag 272 oder das neue SEQUENCE-Objekt) die Leistung beeinträchtigen könnten.
Es könnte die beste Lösung sein, BIGINT anstelle von INT zu verwenden, nur um auf der sicheren Seite zu sein, um die nächsten "Verbesserungen" von MS abzudecken ...
quelle
Rumtscho, wenn Sie nur 1000 Zeilen pro Tag erstellen, gibt es wenig zu entscheiden - verwenden Sie den Datentyp INT mit einem Identitätsfeld und machen Sie es fertig. Eine einfache Rechnung besagt, dass Sie, wenn Sie Ihrer App einen 30-jährigen Lebenszyklus geben (unwahrscheinlich), 200.000 Zeilen pro Tag haben könnten und immer noch im positiven Zahlenbereich eines INT-Datentyps liegen.
Die Verwendung von BigInt ist in Ihrem Fall ein Overkill. Es kann auch Probleme verursachen, wenn auf Ihre App oder Daten über ODBC zugegriffen wird (z. B. in Excel oder MS Access usw.). Bigint übersetzt die meisten ODBC-Treiber nicht gut in Desktop-Apps.
Was GUIDS betrifft, gibt es neben dem zusätzlichen Speicherplatz und den zusätzlichen E / A das große Problem, dass sie von Natur aus nicht sequenziell sind. Wenn sie also Teil eines sortierten Index sind, können Sie davon ausgehen, dass jede Einfügung ausgeführt wird erfordern, dass der Index neu sortiert wird. - Jim
quelle
Gibt es eine Lücke zwischen den verwendeten Werten? Oder die Startwerte sind 10.000 und von da an addieren sich alle 1? Manchmal, wenn die Nummer an Kunden vergeben wird, ist die anfängliche Nummer größer als Null, beispielsweise 1500, sodass der Kunde nicht erkennt, dass das System "neu" ist.
Der Nachteil der Verwendung von bigint anstelle von smallint besteht darin, dass bigint "mehr Speicherplatz" verwendet, wenn beim Lesen von Datenträgern weniger Datenträgerblöcke für jeden Datenträger gelesen werden. Wenn Ihr Zeilenabstand klein ist, kann dies ein Nachteil sein, wenn nicht, spielt es keine Rolle. Es spielt auch keine Rolle, wenn Sie nicht viele Ressourcen gleichzeitig abfragen und über die richtigen Indizes verfügen.
Und wie bereits in einer anderen Antwort erwähnt, sollten Sie sich keine Sorgen machen, wenn Sie befürchten, dass Ihnen die Indizes ausgehen, wenn Sie nicht über ein Millionärsgeschäft verfügen. Das Erfinden eines Mechanismus zum "Wiederherstellen von IDs" ist teuer und fügt der Software Fehlerpunkte und Komplexität hinzu.
Grüße
quelle
Wenn ich Ihr Chef wäre , würden mich die Gründe für die unerwartet hohen ID-Werte am meisten interessieren ... wie ich es sehe, für jedes der beiden von Ihnen beschriebenen Szenarien:
WENN die vorherigen Tests Identitätswerte erhöht haben, würde mich Ihr anderer Kommentar zur erwarteten Anzahl von Datensätzen auch dazu bewegen, einen kleineren Schlüsseltyp vorzuschlagen. Ehrlich gesagt würde ich auch überlegen, ob es möglich ist, die Sequenz zurückzusetzen und vorhandene Datensätze neu zu nummerieren, wenn der Test für die aktuell vorgesehene Verwendung der Tabelle nicht geeignet ist (die meisten würden diesen Overkill in Betracht ziehen - "es hängt davon ab").
WENN die Mehrheit der in die Tabelle geschriebenen Datensätze gelöscht wird, sobald ich beabsichtige, stattdessen zwei Tabellen zu verwenden. eine temporäre Tabelle, in der Aufzeichnungen nicht langfristig aufbewahrt werden, und eine andere, in der nur Aufzeichnungen dauerhaft aufbewahrt werden, die wir erstellen. Ihre Erwartungen bezüglich der Anzahl der Langzeitdatensätze legen mir die Verwendung eines kleineren Typs für Ihre Schlüsselspalte nahe, und einige Datensätze pro Tag führen kaum dazu, dass Sie aufgrund von Leistungsproblemen einen Datensatz von einer Tabelle in eine andere verschieben müssen einer. Ich vermute, dass es nicht Ihr Szenario ist, aber stellen Sie sich vor, dass eine Einkaufswebsite möglicherweise lieber einen Warenkorb / BasketItem verwaltet, und wenn eine Bestellung tatsächlich aufgegeben wird, werden die Daten in den Bestell- / BestellItem-Satz verschoben.
Zusammenfassen; Meiner Meinung nach sind BIGINTs nicht unbedingt zu befürchten, aber für viele Szenarien unnötig groß. Wenn die Tabelle nie groß wird, werden Sie nie bemerken, dass Ihre Auswahl an Datentypen übertrieben war ... aber wenn Sie Tabellen mit Millionen von Zeilen und vielen FK-Spalten haben, die GROSS sind, obwohl sie kleiner hätten sein können - dann möchten Sie vielleicht die Die Typen wurden konservativer ausgewählt (berücksichtigen Sie nicht nur die Schlüsselspalten, sondern auch alle vorderen Schlüsselspalten und alle von Ihnen aufbewahrten Sicherungen usw.). Speicherplatz ist nicht immer günstig (betrachten Sie SAN-Speicher an verwalteten Speicherorten - dh der Speicherplatz wird angemietet).
Im Wesentlichen plädiere ich dafür, Ihre Auswahl des Datentyps immer und nicht manchmal sorgfältig zu überlegen . Sie werden Verwendungsmuster nicht immer richtig vorhersagen, aber ich denke, Sie werden in der Regel bessere Entscheidungen treffen, als immer davon auszugehen, dass "größer ist besser". Im Allgemeinen wähle ich den kleinsten Typ aus, der den erforderlichen und vernünftigen Wertebereich enthalten kann, und ich werde INT, SMALLINT und sogar TINYINT gerne in Betracht ziehen, wenn ich der Meinung bin, dass der Wert in absehbarer Zukunft wahrscheinlich in diesen Typ passt. Es ist jedoch unwahrscheinlich, dass die kleineren Typen mit IDENTITY-Spalten verwendet werden, sie können jedoch problemlos mit Nachschlagetabellen verwendet werden, in denen Schlüsselwerte manuell festgelegt werden.
Schließlich können die von Menschen verwendeten Technologien ihre Erwartungen und Antworten erheblich beeinflussen. Einige Tools verursachen mit größerer Wahrscheinlichkeit Lücken in den Bereichen, z. B. indem Identitätsbereiche pro Prozess vorab gebucht werden. Im Gegensatz dazu schlägt @DocSalvager eine gründliche, überprüfbare Sequenz vor, die den Standpunkt Ihres Chefs widerspiegelt. Ich persönlich habe nie so viel Autorität gefordert - obwohl die allgemeine Regel, dass Identitäten sequentiell und im Allgemeinen lückenlos sind, mir in Support-Situationen und bei der Problemanalyse oft unglaublich nützlich war.
quelle
Die Verwendung
bigint
als Identität und das Leben mit den Lücken:int
immer noch etwa 2 Millionen Tage Daten erhalten. Weitere Seiten müssen gelesen und geschrieben werden. Indizes können tiefer werden. (Bei diesen Mengen ist dies jedoch kein wesentliches Problem).Roll deinen eigenen:
quelle
Wenn Sie wirklich besorgt sind, den oberen Schwellenwert von INT für Ihre PKs zu erreichen, sollten Sie die Verwendung von GUIDs in Betracht ziehen. Ja, ich weiß, dass es 16 Bytes vs 4 Bytes sind, aber die Festplatte ist billig.
Hier ist eine gute Zusammenfassung der Vor- und Nachteile.
quelle
RDBMS-Primärschlüssel (normalerweise als 'ID' bezeichnete Spalte)
In RDBMS-Spalten (Feldern), die automatisch inkrementiert werden, können Lücken nicht vermieden werden. Sie sind in erster Linie dazu gedacht, eindeutige PKs zu erstellen. Aus Gründen der Leistung ordnen die Hauptprodukte diese in Chargen zu, sodass automatische Wiederherstellungsmechanismen für verschiedene normale Betriebsstörungen dazu führen können, dass Zahlen nicht verwendet werden. Das ist normal.
Nicht
unterbrochene Sequenzen Wenn Sie eine nicht unterbrochene Sequenznummer benötigen, wie dies häufig von Benutzern erwartet wird, sollte es sich um eine separate Spalte handeln, die programmgesteuert zugewiesen wird und nicht die PK sein darf . Somit können diese 1000 Datensätze alle dieselbe Nummer in dieser Spalte haben.
Warum wollen Benutzer ununterbrochene Sequenzen?
Fehlende Sequenznummern sind das grundlegendste Fehlerzeichen, das bei jeder Art von Prüfung aufgedeckt wird. Dieses "Bookkeeping-101" -Prinzip ist allgegenwärtig. Was jedoch für eine kleine Anzahl von Datensätzen funktioniert, die von Hand gepflegt werden, hat ein ernstes Problem, wenn es auf eine sehr große Anzahl von Datensätzen in Datenbanken angewendet wird.
Die Wiederverwendung von Schlüsselwerten für nicht verwandte Datensätze macht die Datenbank ungültig. Die
Verwendung der ersten nicht verwendeten Ganzzahl führt die Wahrscheinlichkeit ein, dass eine Zahl zu einem späteren Zeitpunkt für Datensätze wiederverwendet wird, die nicht mit dem Original zusammenhängen. Das macht die Datenbank als genaue Darstellung der Tatsachen unzuverlässig. Dies ist der Hauptgrund dafür, dass Autoinkrementierungsmechanismen absichtlich so konzipiert sind, dass sie einen Wert niemals wiederverwenden.
quelle