Was sind die Best Practices für die Verwendung einer GUID als Primärschlüssel, insbesondere in Bezug auf die Leistung?

336

Ich habe eine Anwendung, die GUID als Primärschlüssel in fast allen Tabellen verwendet, und ich habe gelesen, dass es Probleme mit der Leistung gibt, wenn GUID als Primärschlüssel verwendet wird. Ehrlich gesagt habe ich kein Problem gesehen, aber ich bin dabei, eine neue Anwendung zu starten, und ich möchte weiterhin die GUIDs als Primärschlüssel verwenden, aber ich habe darüber nachgedacht, einen zusammengesetzten Primärschlüssel (die GUID und möglicherweise ein anderes Feld) zu verwenden .)

Ich verwende eine GUID, weil sie nett und einfach zu verwalten sind, wenn Sie unterschiedliche Umgebungen wie "Produktions" -, "Test" - und "Entwickler" -Datenbanken haben und auch für Migrationsdaten zwischen Datenbanken.

Ich werde Entity Framework 4.3 verwenden und möchte die Guid im Anwendungscode zuweisen, bevor ich sie in die Datenbank einfüge. (dh ich möchte nicht, dass SQL die Guid generiert).

Was ist die beste Vorgehensweise zum Erstellen von GUID-basierten Primärschlüsseln, um die mit diesem Ansatz verbundenen vermeintlichen Leistungseinbußen zu vermeiden?

VAAA
quelle
20
Das Problem wird nicht angenommen. Wenn Ihre PK geclustert ist, kann fast jede Einfügung einen Seitensplit verursachen. In modernen Versionen von SQL Server wurde dies mit NEWSEQUENTIALID () "behoben", dies verliert jedoch den Vorteil, dass es zuvor berechnet werden kann. Ich empfehle Ihnen dringend, sich an anderer Stelle über GUIDs zu informieren, da dies eine viel zu weit gefasste Frage ist und wahrscheinlich einen religiösen Kampf auslösen wird, der stundenlang andauern wird ...
Aaron Bertrand,
4
Ich möchte auch hinzufügen, dass der Wortserver nicht eindeutig ist, da ich die Guid auf der Serverseite zuweisen möchte (ich möchte nicht, dass SQL die GUID erstellt) .
Erik Philips
Diese Frage hat Ähnlichkeiten mit diesem "SQL-Server-Guid-Sort-Algorithmus-Warum" stackoverflow.com/questions/7810602/…
Clinton Ward

Antworten:

494

GUIDs scheinen eine natürliche Wahl für Ihren Primärschlüssel zu sein - und wenn Sie es wirklich müssen, könnten Sie wahrscheinlich argumentieren, sie für den PRIMARY KEY der Tabelle zu verwenden. Ich würde dringend empfehlen , die GUID-Spalte nicht als Clustering-Schlüssel zu verwenden , was SQL Server standardmäßig tut, es sei denn, Sie weisen ausdrücklich an, dies nicht zu tun .

Sie müssen wirklich zwei Punkte auseinander halten:

  1. Der Primärschlüssel ist ein logisches Konstrukt - einer der Kandidatenschlüssel, der jede Zeile in Ihrer Tabelle eindeutig und zuverlässig identifiziert. Dies kann wirklich alles sein - eine INT, eine GUID, eine Zeichenfolge - wählen Sie aus, was für Ihr Szenario am sinnvollsten ist.

  2. der Clustering-Schlüssel (die Spalte oder Spalten, die den "Clustered-Index" in der Tabelle definieren) - dies ist eine Sache, die sich auf den physischen Speicher bezieht, und hier ist ein kleiner, stabiler, ständig wachsender Datentyp Ihre beste Wahl - INToder BIGINTals Ihre Standardoption.

Standardmäßig wird der Primärschlüssel in einer SQL Server-Tabelle auch als Clustering-Schlüssel verwendet - aber das muss nicht so sein! Ich persönlich habe massive Leistungssteigerungen festgestellt, als der vorherige GUID-basierte Primär- / Clusterschlüssel in zwei separate Schlüssel aufgeteilt wurde - den Primärschlüssel (logisch) in der GUID und den Clusterschlüssel (Reihenfolge) in einer separaten INT IDENTITY(1,1)Spalte.

Wie Kimberly Tripp - die Königin der Indizierung - und andere schon oft gesagt haben - a, GUIDda der Clustering-Schlüssel nicht optimal ist, da er aufgrund seiner Zufälligkeit zu einer massiven Fragmentierung von Seiten und Indizes und zu einer allgemein schlechten Leistung führt.

Ja, ich weiß - es gibt newsequentialid()in SQL Server 2005 und höher -, aber selbst das ist nicht wirklich und vollständig sequentiell und leidet daher auch unter den gleichen Problemen wie das GUID- nur ein bisschen weniger prominent.

Dann ist noch ein weiteres Problem zu berücksichtigen: Der Clustering-Schlüssel in einer Tabelle wird jedem Eintrag in jedem nicht geclusterten Index in Ihrer Tabelle hinzugefügt. Sie möchten also wirklich sicherstellen, dass er so klein wie möglich ist. In der Regel sollte ein INTmit 2+ Milliarden Zeilen für die überwiegende Mehrheit der Tabellen ausreichen - und im Vergleich zu einem GUIDals Clustering-Schlüssel können Sie sich Hunderte von Megabyte Speicherplatz auf der Festplatte und im Serverspeicher sparen.

Schnelle Berechnung - Verwenden von INTvs. GUIDals Primär- und Clustering-Schlüssel:

  • Basistabelle mit 1'000'000 Zeilen (3,8 MB gegenüber 15,26 MB)
  • 6 nicht gruppierte Indizes (22,89 MB gegenüber 91,55 MB)

GESAMT: 25 MB vs. 106 MB - und das nur auf einem Tisch!

Noch ein Denkanstoß - exzellentes Zeug von Kimberly Tripp - lesen Sie es, lesen Sie es noch einmal, verdauen Sie es! Es ist wirklich das Evangelium der SQL Server-Indizierung.

PS: Wenn Sie es nur mit ein paar hundert oder ein paar tausend Zeilen zu tun haben, haben die meisten dieser Argumente natürlich keinen großen Einfluss auf Sie. Allerdings: Wenn Sie in die Dutzende oder Hunderte von Tausenden von Zeilen, oder Sie starten das Zählen in Millionen - dann werden diese Punkte sehr wichtig und sehr wichtig zu verstehen.

Update: Wenn Sie Ihre PKGUIDSpalte als Primärschlüssel (aber nicht als Clustering-Schlüssel) und eine andere Spalte MYINT( INT IDENTITY) als Clustering-Schlüssel verwenden möchten, verwenden Sie Folgendes:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Grundsätzlich gilt: Sie müssen der Einschränkung nur explizit mitteilen PRIMARY KEY, dass dies der Fall ist NONCLUSTERED(andernfalls wird sie standardmäßig als Clustered-Index erstellt). Anschließend erstellen Sie einen zweiten Index, der als definiert istCLUSTERED

Dies funktioniert - und es ist eine gültige Option, wenn Sie ein vorhandenes System haben, das für die Leistung "überarbeitet" werden muss. Wenn Sie bei einem neuen System von vorne beginnen und sich nicht in einem Replikationsszenario befinden, würde ich immer ID INT IDENTITY(1,1)meinen Cluster-Primärschlüssel auswählen - viel effizienter als alles andere!

marc_s
quelle
2
Dies ist eine großartige Antwort. Eine Sache, die ich erwähnen möchte, ist, dass es häufig nützlich ist, den Schlüssel vor dem Einfügen zu generieren. Die Verwendung von "newsequentialid ()" kann beim Clustering hilfreich sein, erfordert jedoch einen zusätzlichen Roundtrip zu SQL. Ein weiterer Vorteil des "Ersatzschlüssel" -Ansatzes besteht darin, dass Sie clientseitig neue IDs mit weniger Bedenken hinsichtlich der Indexfragmentierung generieren können.
Andrew Theken
2
Die Art und Weise, wie ich dies lese, ist, dass FKs, die sowohl eine nicht gruppierte eindeutige Identifizierungsspalte als auch die int-Identitätsspalte haben, auch eindeutige Identifizierer sein sollten. Wenn Sie das tun, wann würden Sie die Identitätsspalte tatsächlich direkt verwenden oder nicht?
pinkfloydx33
2
Kleine Frage, sollte die GUID jetzt für Joins verwendet werden, oder die int id? Mein Instinkt sagt mir, dass die GUID verwendet werden sollte, aber ich sehe kein technisches Problem mit der int id ...
Nicolas Belley
3
@marc_s, aber sollten wir in einem Replikationsszenario, wenn die int-Spalte Identität ist, nicht die GUID verwenden, da sich die int-Spalte geräteübergreifend wiederholen kann?
Nicolas Belley
6
@Kipei: die wichtigsten Fragen ist die IF Sie einen solchen natürlichen Wert haben - dann ja, können Sie es als Primärschlüssel verwenden. ABER : Werte wie DATETIMEzum Beispiel sind für einen Clustering-Schlüssel NICHT nützlich, da sie nur eine Genauigkeit von 3,33 ms haben und daher Duplikate existieren können. In einem solchen Fall * benötigen Sie INT IDENTITYstattdessen immer noch einen - daher verwende ich normalerweise standardmäßig einen wirklich verwendbaren natürlichen Schlüssel , der aufgrund meiner über 20-jährigen Erfahrung kaum jemals wirklich existiert ...
marc_s
51

Ich verwende GUIDs seit 2005 als PKs. In dieser verteilten Datenbankwelt ist dies der absolut beste Weg, verteilte Daten zusammenzuführen. Sie können Zusammenführungstabellen auslösen und vergessen, ohne sich Sorgen machen zu müssen, dass Ints über verknüpfte Tabellen hinweg übereinstimmen. GUIDs-Joins können ohne Bedenken kopiert werden.

Dies ist mein Setup für die Verwendung von GUIDs:

  1. PK = GUID. GUIDs werden ähnlich wie Zeichenfolgen indiziert, sodass Tabellen mit hohen Zeilen (über 50 Millionen Datensätze) möglicherweise eine Tabellenpartitionierung oder andere Leistungstechniken erfordern. SQL Server wird immer effizienter, sodass Leistungsprobleme immer weniger zutreffen.

  2. PK Guid ist ein NON-Clustered-Index. Indizieren Sie niemals eine GUID im Cluster, es sei denn, es handelt sich um NewSequentialID. Aber selbst dann führt ein Neustart des Servers zu größeren Unterbrechungen bei der Bestellung.

  3. Fügen Sie jeder Tabelle ClusterID Int hinzu. Dies ist Ihr CLUSTERED Index ... der Ihren Tisch bestellt.

  4. Das Beitreten zu ClusterIDs (int) ist effizienter, aber ich arbeite mit 20 bis 30 Millionen Datensatztabellen, sodass das Beitreten zu GUIDs die Leistung nicht sichtbar beeinträchtigt. Wenn Sie maximale Leistung erzielen möchten, verwenden Sie das ClusterID-Konzept als Primärschlüssel und treten Sie der ClusterID bei.

Hier ist meine E-Mail-Tabelle ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Gut
quelle
Könnten Sie die PK_Email-Einschränkung erklären? Warum haben Sie ... NonClustered (EmailID ASC) statt ... Nonclustered (ClusterID ASC)?
Phil
2
Sie wetten. Zwei wichtige Dinge, die mit Indizes geschehen: 1. Clustered on ClusterID - Ordnet Ihre Tabelle auf der Festplatte an (0% Fragmentierung). 2. Nicht auf ClusterID gruppiert - Indiziert das Feld EmailID, um die Suche nach GUID-IDs zu beschleunigen. Eine GUID-Feldsuche verhält sich string-ish, sodass eine EmailID-Suche ohne den Index langsam wäre.
Robert J. Good
@ RobertJ.Good Ich habe diese Methode bereits besprochen gesehen, dh das Hinzufügen eines Ersatz-Int-Schlüssels zum Clustering. Aber ich kann nirgendwo etwas finden, was den Leistungsgewinn zeigt, wenn ein Ersatzschlüssel-Clustered-Index über die Verwendung eines Heaps verfügt. Haben Sie Links zu Benchmark-Daten?
Dale K
1
Hallo @ DaleBurrell, der Clustered-Index soll die Fragmentierung von Tabellen verhindern. Leistungssteigerung tritt auf, wenn die Tabelle auf natürliche Weise in der Reihenfolge auf der Festplatte mit geringer Fragmentierung wächst.
Robert J. Good
@ RobertJ.Good Ist das eine Webanwendung? Was verwenden Sie in URLs / Hrefs? Guid oder Int?
Dariol
10

Ich entwickle gerade eine Webanwendung mit EF Core und hier ist das Muster, das ich verwende:

Alle meine Klassen (Tabellen) und eine int PK und FK. Ich habe eine zusätzliche Spalte vom Typ Guid (vom c # -Konstruktor generiert) mit einem nicht gruppierten Index.

Alle Verknüpfungen der Tabelle in EF werden über die int-Schlüssel verwaltet, während der gesamte Zugriff von außen (Controller) mit den Guids erfolgt.

Diese Lösung ermöglicht es, die int-Schlüssel nicht in URLs anzuzeigen, sondern das Modell sauber und schnell zu halten.

EricImhauser
quelle
Müssen Sie etwas tun, um die Ganzzahl pK als Cluster zu konfigurieren, z. B. Datenanmerkungen, oder wird sie nur automatisch konfiguriert?
Allen Wang
Wie lautet der Name der Immobilie, die Sie für Guid One verwenden?
Trong Phan
3

Wenn Sie die GUID als Primärschlüssel verwenden und einen Clustered-Index erstellen, empfehle ich, den Standardwert NEWSEQUENTIALID () zu verwenden

AnandPhadke
quelle
warum würdest du das tun?
echtfafa
3

Dieser Link sagt es besser als ich könnte und half bei meiner Entscheidungsfindung. Normalerweise entscheide ich mich für ein int als Primärschlüssel, es sei denn, ich muss dies unbedingt tun, und ich lasse SQL Server dieses Feld automatisch generieren / verwalten, es sei denn, ich habe einen bestimmten Grund, dies nicht zu tun. In der Realität müssen Leistungsprobleme basierend auf Ihrer spezifischen App ermittelt werden. Hier spielen viele Faktoren eine Rolle, einschließlich, aber nicht beschränkt auf die erwartete Datenbankgröße, die ordnungsgemäße Indizierung, die effiziente Abfrage und vieles mehr. Obwohl die Leute anderer Meinung sein mögen, werden Sie in vielen Szenarien bei beiden Optionen keinen Unterschied bemerken und Sie sollten auswählen, was für Ihre App besser geeignet ist und was es Ihnen ermöglicht, einfacher, schneller und effektiver zu entwickeln (wenn Sie die App nie fertigstellen) Welchen Unterschied macht der Rest :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS Ich bin mir nicht sicher, warum Sie eine Composite PK verwenden würden oder welchen Nutzen Sie davon haben würden.

Matt
quelle
Stimme voll und ganz zu !! Aber das bedeutet, dass wenn ich eine GUID als PK oder eine zusammengesetzte PK mit GUID und einem anderen Feld habe, das gleiche sein wird, oder?
VAAA
1
Der PK (Index) würde aus den beiden Spalten bestehen. Wenn Sie jedoch keinen geschäftsspezifischen Grund dafür haben, erscheint dies unnötig.
Matt
1
Übrigens ist diese Frage eine der polarisierendsten und umstrittensten Fragen da draußen und daher äußerst schwierig, eine Antwort darauf zu finden, mit der Sie sich zu 100% wohl fühlen werden. Jede Methode bringt Kompromisse mit sich, also viel Glück :)
Matt
0

Sequentielle IDs erleichtern es einem Hacker oder Data Miner erheblich, Ihre Site und Ihre Daten zu gefährden. Beachten Sie dies, wenn Sie eine PK für eine Website auswählen.

DaBlue
quelle
Können Sie eine Logik oder einen Beweis liefern, um diese Behauptung zu stützen? Ich habe Probleme zu sehen, wie eine sequentielle ID die Sicherheit gefährden kann.
Jonaglon
Sicher, wenn Sie wissen, dass ID-Nummern ganzzahlig sind, können Sie nacheinander Datensätze in einer Datenbank erraten. Wenn Sie also ein einzelnes Element abfragen, können Sie sagen, dass das nächste Element pk + 1 ist. Wenn Sie zufällige GUIDS haben, folgt es keinem Muster. Es wäre fast unmöglich, andere Datensätze als die zuvor abgefragten abzufragen (und die PK zu kennen).
DaBlue
1
Wenn ein Hacker Ihre Datenbank abfragen kann, sind Sie bereits kompromittiert. Ich sehe nicht, wie sequentielle IDs die Situation verschlimmern.
Jonaglon
1
Wenn ein Benutzer 1012 gegen eine andere Nummer austauschen und Daten anzeigen kann, die er nicht sollte, liegt ein sehr schwerwiegendes Sicherheitsproblem vor. Dieses Problem wird nicht durch die Auswahl des Primärschlüssels verursacht, sondern durch dieses verstärkt. Ich verstehe Ihren Standpunkt, danke, dass Sie ihn formuliert haben.
Jonaglon
2
Sie können eine GUID verwenden, um einen Datensatz auf der Webseite zu finden, bei dem es sich nicht um die PK der Tabelle handelt. Die Verwendung von Abfrageparametern auf einer Website sollte nicht definieren, wie Sie Ihr DB-Schema strukturieren. Die PK hat nichts mit Eingaben und Parametern in der Benutzeroberfläche oder im Backend-System zu tun.
Panos Roditakis