Meine Entwickler haben ihre Anwendung so eingerichtet, dass sie GUIDs als PK für fast alle ihre Tabellen verwendet. Standardmäßig hat SQL Server den Clustered-Index für diese PKs eingerichtet.
Das System ist relativ jung und unsere größten Tabellen haben etwas mehr als eine Million Zeilen. Wir werfen jedoch einen Blick auf unsere Indizierung und möchten in der Lage sein, schnell zu skalieren, wenn dies in naher Zukunft erforderlich sein könnte.
Daher bestand meine erste Neigung darin, den Clustered-Index in das erstellte Feld zu verschieben, das eine große Repräsentation einer DateTime ist. Die einzige Möglichkeit, die CX eindeutig zu machen, besteht darin, die GUID-Spalte in diese CX einzuschließen, die Reihenfolge wird jedoch zuerst erstellt.
Würde dies den Clustering-Schlüssel zu breit machen und die Leistung für Schreibvorgänge steigern? Lesen ist ebenfalls wichtig, aber Schreiben ist an dieser Stelle wahrscheinlich ein größeres Problem.
newsequentialid
werden, zufällig sind. Clustered Keys sind am besten geeignet, wenn sie schmal und ansteigend sind. Eine GUID ist das Gegenteil: fett und zufällig. Stellen Sie sich ein Bücherregal vor, das fast voller Bücher ist. In kommt die OED und wegen der Zufälligkeit der Guids, fügt es in der Mitte des Regals. Um die Ordnung zu halten, muss die rechte Hälfte der Bücher an einen neuen Ort verschoben werden, was eine zeitintensive Aufgabe ist. Das ist es, was die GUID mit Ihrer Datenbank macht und die Leistung tötet.Antworten:
Die Hauptprobleme bei GUIDs, insbesondere bei nicht sequentiellen, sind:
Was bedeutet das für Ihre Situation? Es kommt auf Ihr Design an. Wenn es in Ihrem System nur um Schreibvorgänge geht und Sie keine Bedenken hinsichtlich des Datenabrufs haben, ist der von Thomas K skizzierte Ansatz korrekt. Sie müssen jedoch berücksichtigen, dass durch die Verfolgung dieser Strategie viele potenzielle Probleme beim Lesen und Speichern dieser Daten entstehen. Wie Jon Seigel hervorhebt , werden Sie auch mehr Platz einnehmen und im Wesentlichen Gedächtnisschwund haben.
Die Hauptfrage bei GUIDs ist, wie notwendig sie sind. Entwickler mögen sie, weil sie für globale Einzigartigkeit sorgen, aber es kommt selten vor, dass diese Art von Einzigartigkeit notwendig ist. Beachten Sie jedoch, dass Sie möglicherweise nicht den richtigen Datentyp für Ihren Schlüssel verwenden, wenn Ihre maximale Anzahl von Werten weniger als 2.147.483.647 beträgt (der maximale Wert einer 4-Byte-Ganzzahl mit Vorzeichen). Selbst bei Verwendung von BIGINT (8 Byte) beträgt Ihr Maximalwert 9.223.372.036.854.775.807. Dies ist in der Regel für alle nicht-globalen Datenbanken (und für viele globale Datenbanken) ausreichend, wenn Sie einen Wert für die automatische Inkrementierung für einen eindeutigen Schlüssel benötigen.
Was schließlich die Verwendung eines Heapspeichers im Vergleich zu einem Clustered-Index betrifft, ist ein Heapspeicher beim reinen Schreiben von Daten am effizientesten, da Sie den Aufwand für Einfügungen minimieren. Heaps in SQL Server sind jedoch für das Abrufen von Daten äußerst ineffizient. Ich habe die Erfahrung gemacht, dass ein Clustered-Index immer wünschenswert ist, wenn Sie die Möglichkeit haben, einen zu deklarieren. Ich habe gesehen, dass das Hinzufügen eines Clustered-Index zu einer Tabelle (4 Milliarden + Datensätze) die Gesamtselektionsleistung um den Faktor 6 verbessert hat.
Zusätzliche Information:
quelle
Die GUID als Schlüssel und Cluster in einem OLTP-System ist nicht fehlerhaft (es sei denn, Sie haben viele Indizes in der Tabelle, die unter der erhöhten Größe des Clusters leiden). Tatsächlich sind sie viel skalierbarer als IDENTITY-Spalten.
Es ist weit verbreitet, dass GUIDs in SQL Server ein großes Problem darstellen - im Großen und Ganzen ist dies ganz einfach falsch. Tatsächlich kann GUID auf Boxen mit mehr als 8 Kernen deutlich skalierbarer sein:
Es tut mir leid, aber Ihre Entwickler haben Recht. Sorgen Sie sich um andere Dinge, bevor Sie sich um GUID sorgen.
Oh, und zum Schluss: Warum möchten Sie überhaupt einen Cluster-Index? Wenn es sich bei Ihrem Unternehmen um ein OLTP-System mit vielen kleinen Indizes handelt, sind Sie mit einem Haufen wahrscheinlich besser dran.
Betrachten wir nun, was die Fragmentierung (die die GUID einführt) für Ihre Lesevorgänge bewirkt. Es gibt drei Hauptprobleme bei der Fragmentierung:
Da es bei Ihrer Frage um Skalierbarkeit geht, die wir als "Hinzufügen von mehr Hardware beschleunigt das System" definieren können, sind dies die geringsten Probleme. Um jeden der Reihe nach anzusprechen
Ad 1) Wenn Sie skalieren möchten, können Sie es sich leisten, I / O zu kaufen. Selbst eine billige Samsung / Intel 512 GB SSD (für ein paar USD / GB) bringt Ihnen weit über 100.000 IOPS. Bei einem 2-Socket-System werden Sie das so schnell nicht verbrauchen. Und wenn Sie darauf stoßen sollten, kaufen Sie noch einen und Sie sind bereit
Ad 2) Wenn Sie in Ihrer Tabelle löschen, haben Sie trotzdem halb volle Seiten. Und selbst wenn Sie dies nicht tun, ist Speicher billig und für alle außer den größten OLTP-Systemen geeignet - die aktuellen Daten sollten dort passen. Der Versuch, mehr Daten in Seiten zu packen, führt zu einer Suboptimierung, wenn Sie nach Skalierung suchen.
Zu 3) Eine Tabelle, die aus häufig seitenweise aufgeteilten, stark fragmentierten Daten besteht, führt zufällige E / A-Vorgänge mit genau der gleichen Geschwindigkeit aus wie eine sequentiell gefüllte Tabelle
In Bezug auf das Beitreten gibt es zwei Haupt-Join-Typen, die Sie wahrscheinlich in einer OLTP-ähnlichen Workload sehen werden: Hash und Schleife. Schauen wir uns diese der Reihe nach an:
Hash-Join: Bei einem Hash-Join wird davon ausgegangen, dass der kleine Tisch gescannt wird und in der Regel der größere gesucht wird. Es ist sehr wahrscheinlich, dass kleine Tabellen im Arbeitsspeicher vorhanden sind, daher ist die E / A hier nicht Ihr Anliegen. Wir haben bereits darauf hingewiesen, dass Suchanfragen im fragmentierten Index dieselben Kosten verursachen wie im nicht fragmentierten Index
Loop Join: Der äußere Tisch wird gesucht. Gleiche Kosten
Möglicherweise werden auch viele schlechte Tabellen gescannt - aber die GUID ist wiederum nicht Ihr Problem, sondern die richtige Indizierung.
Möglicherweise werden jetzt einige legitime Bereichsüberprüfungen durchgeführt (insbesondere beim Verknüpfen von Fremdschlüsseln). In diesem Fall sind die fragmentierten Daten im Vergleich zu den nicht fragmentierten Daten weniger "gepackt". Aber lassen Sie uns überlegen, welche Verknüpfungen Sie wahrscheinlich in gut indizierten 3NF-Daten sehen werden:
Ein Join aus einer Tabelle, der einen Fremdschlüsselverweis auf den Primärschlüssel der Tabelle enthält, auf die er verweist
Umgekehrt
Anzeige 1) In diesem Fall führen Sie eine einzelne Suche zum Primärschlüssel durch - Verbinden von n mit 1. Fragmentierung oder nicht, gleiche Kosten (eine Suche)
Zu 2) In diesem Fall verbinden Sie sich mit demselben Schlüssel, können jedoch mehr als eine Zeile abrufen (Bereichssuche). Der Join ist in diesem Fall 1 bis n. Bei der von Ihnen gesuchten Fremdtabelle wird jedoch nach dem gleichen Schlüssel gesucht, der sich in einem fragmentierten Index mit der gleichen Wahrscheinlichkeit auf derselben Seite wie auf einer nicht fragmentierten Seite befindet.
Betrachten Sie diese Fremdschlüssel für einen Moment. Selbst wenn Sie unsere Primärschlüssel "perfekt" sequentiell gelegt hätten - alles, was auf diesen Schlüssel zeigt, ist immer noch nicht sequentiell.
Natürlich können Sie auf einer virtuellen Maschine in einem SAN in einer Bank laufen, die wenig Geld und viel Prozess hat. Dann geht all dieser Rat verloren. Aber wenn das Ihre Welt ist, ist Skalierbarkeit wahrscheinlich nicht das, wonach Sie suchen - Sie suchen Leistung und hohe Geschwindigkeit / Kosten -, beides verschiedene Dinge.
quelle
Thomas: Einige deiner Punkte sind absolut sinnvoll und ich stimme ihnen allen zu. Wenn Sie sich auf SSDs befinden, ändert sich das Gleichgewicht zwischen dem, wofür Sie optimieren. Random vs Sequential ist nicht die gleiche Diskussion wie Spinning Disk.
Insbesondere stimme ich zu, dass eine reine DB-Sichtweise fürchterlich falsch ist. Ihre Anwendung langsam und unbezwingbar machen zu verbessern einfach die DB-Leistung kann dies zu .
Das große Problem mit der IDENTITÄT (oder der Sequenz oder irgendetwas anderem) das in der DB generiert wurde) ist, dass es schrecklich langsam ist, da es einen zur DB erfordert, um einen Schlüssel zu erstellen. Dies führt automatisch zu einem Engpass in Ihrer DB und erzwingt, dass Anwendungen erforderlich sind einen DB-Aufruf tätigen, um die Verwendung einer Taste zu starten. Das Erstellen einer GUID löst dieses Problem, indem die Anwendung zum Erstellen des Schlüssels verwendet wird. Es ist garantiert (per Definition) global eindeutig, und die Anwendungsebenen können damit den Datensatz weitergeben, bevor ein DB-Roundtrip durchgeführt wird.
Ich verwende jedoch eher eine Alternative zu GUIDs. Meine persönliche Präferenz für einen Datentyp ist ein global eindeutiger BIGINT, der von der App generiert wird. Wie geht man dabei vor? Im einfachsten Beispiel fügen Sie Ihrer App eine kleine, SEHR leichte Funktion hinzu, um eine GUID zu hashen. Angenommen, Ihre Hash-Funktion ist schnell und relativ schnell (siehe CityHash von Google für ein Beispiel: http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html - stellen Sie sicher, dass Sie alle Kompilierungsschritte richtig ausführen. oder die FNV1a-Variante von http://tools.ietf.org/html/draft-eastlake-fnv-03 für einfachen Code) Ihnen den Vorteil, dass sowohl anwendungsgenerierte eindeutige Bezeichner als auch ein 64-Bit-Schlüsselwert, mit dem CPUs besser arbeiten, zur Verfügung stehen .
Es gibt andere Möglichkeiten, BIGINTs zu generieren, und in beiden Algen besteht die Möglichkeit von Hash-Kollisionen - lesen Sie und treffen Sie bewusste Entscheidungen.
quelle