Nachdem ich diese Frage zum Vergleichen von sequentiellen und nicht-sequentiellen GUIDs gestellt hatte, versuchte ich, die INSERT-Leistung für 1) eine Tabelle mit einem GUID-Primärschlüssel, der sequentiell mit initialisiert wurde newsequentialid()
, und 2) eine Tabelle mit einem INT-Primärschlüssel, der sequentiell mit initialisiert wurde, zu vergleichen identity(1,1)
. Ich würde davon ausgehen, dass Letzteres aufgrund der geringeren Breite von Ganzzahlen am schnellsten ist, und es scheint auch einfacher zu sein, eine sequentielle Ganzzahl als eine sequentielle GUID zu generieren. Zu meiner Überraschung waren INSERTs in der Tabelle mit dem Integer-Schlüssel erheblich langsamer als in der sequentiellen GUID-Tabelle.
Dies zeigt die durchschnittliche Zeitnutzung (ms) für die Testläufe:
NEWSEQUENTIALID() 1977
IDENTITY() 2223
Kann mir jemand das erklären?
Das folgende Experiment wurde verwendet:
SET NOCOUNT ON
CREATE TABLE TestGuid2 (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
CREATE TABLE TestInt (Id Int NOT NULL identity(1,1) PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))
DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000
WHILE (@BatchCounter <= 20)
BEGIN
BEGIN TRAN
DECLARE @LocalCounter INT = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestGuid2 (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @LocalCounter = 0
WHILE (@LocalCounter <= @NumRows)
BEGIN
INSERT TestInt (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
SET @LocalCounter +=1
END
SET @BatchCounter +=1
COMMIT
END
DBCC showcontig ('TestGuid2') WITH tableresults
DBCC showcontig ('TestInt') WITH tableresults
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [NEWSEQUENTIALID()]
FROM TestGuid2
GROUP BY batchNumber
SELECT batchNumber,DATEDIFF(ms,MIN(SomeDate),MAX(SomeDate)) AS [IDENTITY()]
FROM TestInt
GROUP BY batchNumber
DROP TABLE TestGuid2
DROP TABLE TestInt
UPDATE: Wenn ich das Skript ändere, um die Einfügungen basierend auf einer TEMP-Tabelle durchzuführen, wie in den Beispielen von Phil Sandler, Mitch Wheat und Martin unten, finde ich auch, dass IDENTITY schneller ist, als es sein sollte. Dies ist jedoch nicht die herkömmliche Methode zum Einfügen von Zeilen, und ich verstehe immer noch nicht, warum das Experiment zunächst fehlgeschlagen ist: Auch wenn ich in meinem ursprünglichen Beispiel GETDATE () weglasse, ist IDENTITY () immer noch viel langsamer. Die einzige Möglichkeit, IDENTITY () besser als NEWSEQUENTIALID () zu machen, besteht darin, die Zeilen für das Einfügen in eine temporäre Tabelle vorzubereiten und die vielen Einfügungen als Batch-Insert mit dieser temporären Tabelle auszuführen. Alles in allem glaube ich nicht, dass wir eine Erklärung für das Phänomen gefunden haben, und IDENTITY () scheint für die meisten praktischen Anwendungen immer noch langsamer zu sein. Kann mir jemand das erklären?
quelle
INT IDENTITY
IDENTITY
ist keine Tabellensperre erforderlich. Konzeptionell konnte ich sehen, dass Sie vielleicht erwarten, dass es MAX (id) + 1 nimmt, aber in Wirklichkeit wird der nächste Wert gespeichert. Eigentlich sollte es schneller gehen, als die nächste GUID zu finden.Antworten:
Ich habe den Code von @Phil Sandler geändert, um den Effekt des Aufrufs von GETDATE () zu entfernen (möglicherweise sind Hardware-Effekte / Interrupts beteiligt?), Und die Zeilen auf dieselbe Länge gebracht.
[Seit SQL Server 2000 gab es mehrere Artikel zu Zeitproblemen und hochauflösenden Zeitgebern, daher wollte ich diesen Effekt minimieren.]
In einem einfachen Wiederherstellungsmodell mit Daten- und Protokolldatei, deren Größe sich nach den Anforderungen richtet, sind hier die Zeitangaben (in Sekunden): (Mit neuen Ergebnissen basierend auf dem unten angegebenen genauen Code aktualisiert.)
Der verwendete Code:
Nachdem ich die Untersuchung von @ Martin gelesen hatte, lief ich in beiden Fällen erneut mit dem vorgeschlagenen TOP (@num), d. H
und hier sind die Timing-Ergebnisse:
Ich konnte den tatsächlichen Ausführungsplan nicht abrufen, da die Abfrage nie zurückgegeben wurde! Es scheint, dass ein Fehler wahrscheinlich ist. (Ausführen von Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64))
quelle
SORT
Operator für die GUIDs?NEWSEQUENTIALID
sowieso jemals verwenden möchte . Es wird den Index vertiefen, 20% mehr Datenseiten im OP-Fall verwenden und wird nur garantiert immer größer, bis die Maschine neu gestartet wirdidentity
. Es scheint in diesem Fall nur, dass der Abfrageplan einen weiteren unnötigen hinzufügt!Bei einer neuen Datenbank in einem einfachen Wiederherstellungsmodell mit einer Datendatei mit einer Größe von 1 GB und einer Protokolldatei mit einer Größe von 3 GB (Laptop-Computer, beide Dateien auf demselben Laufwerk) und einem Wiederherstellungsintervall von 100 Minuten (um zu vermeiden, dass ein Prüfpunkt die Ergebnisse verzerrt) sehe ich ähnliche Ergebnisse zu Ihnen mit der einzelnen Reihe
inserts
.Ich habe drei Fälle getestet: In jedem Fall habe ich 20 Stapel mit jeweils 100.000 Zeilen in die folgenden Tabellen eingefügt. Die vollständigen Skripte finden Sie im Revisionsverlauf dieser Antwort .
Für die dritte Tabelle fügte der Test Zeilen mit einem inkrementierenden
Id
Wert ein, der jedoch selbst berechnet wurde, indem der Wert einer Variablen in einer Schleife inkrementiert wurde.Die Mittelung der über die 20 Chargen genommenen Zeit ergab die folgenden Ergebnisse.
Fazit
Es scheint also definitiv ein Overhead des
identity
Erstellungsprozesses zu sein, der für die Ergebnisse verantwortlich ist. Für die selbst berechnete inkrementierende Ganzzahl stimmen die Ergebnisse viel besser mit den erwarteten Ergebnissen überein, wenn nur die E / A-Kosten berücksichtigt werden.Wenn ich den oben beschriebenen Einfügungscode in gespeicherte Prozeduren einfüge und überprüfe
sys.dm_exec_procedure_stats
, werden die folgenden Ergebnisse angezeigtIn diesen Ergebnissen
total_worker_time
liegt es also um 30% höher. Dies stellt darEs sieht also einfach so aus, als ob der Code, der den
IDENTITY
Wert generiert, mehr CPU-NEWSEQUENTIALID()
Kapazität hat als der Code, der den Wert generiert ausreichend hoch, um die zusätzlichen logischen Lese- und Schreibvorgänge aufzuwiegen, die aufgrund der größeren Schlüsselbreite anfallen. (Anmerkung: Itzik Ben Gan hat hier ähnliche Tests durchgeführt und eine 2µs Strafe pro Insert festgestellt.)Warum ist
IDENTITY
die CPU also intensiver alsUuidCreateSequential
?Ich glaube, das wird in diesem Artikel erklärt . Für jeden zehnten
identity
generierten Wert muss SQL Server die Änderung in die Systemtabellen auf der Festplatte schreibenWas ist mit MultiRow Inserts?
Wenn die 100.000 Zeilen in eine einzelne Anweisung eingefügt werden, verschwindet der Unterschied mit vielleicht noch geringem Vorteil für den
GUID
Fall, aber bei weitem nicht so eindeutigen Ergebnissen. Der Durchschnitt für 20 Chargen in meinem Test warDer Grund dafür, dass es nicht den Nachteil gibt, der in Phils Code und Mitchs erstem Satz von Ergebnissen zu sehen ist, ist, dass der Code, den ich für die mehrzeilige Einfügung verwendet habe, zufällig verwendet wurde
SELECT TOP (@NumRows)
. Dadurch konnte das Optimierungsprogramm die Anzahl der einzufügenden Zeilen nicht richtig schätzen.Dies scheint von Vorteil zu sein, da es einen bestimmten Wendepunkt gibt, an dem eine zusätzliche Sortieroperation für die (angeblich sequentiellen!)
GUID
S hinzugefügt wird .Diese Sortieroperation ist aus dem erläuternden Text in BOL nicht erforderlich .
Daher schien es mir ein Fehler oder eine fehlende Optimierung zu sein, dass SQL Server nicht erkennt, dass die Ausgabe des Computing-Skalars bereits vorsortiert ist, wie dies anscheinend bereits für die
identity
Spalte der Fall ist . ( Bearbeiten Ich habe dies gemeldet und das unnötige Sortierproblem ist jetzt in Denali behoben. )quelle
Ganz einfach: Mit GUID ist es billiger, die nächste Nummer in der Zeile zu generieren als mit IDENTITY (Der aktuelle Wert der GUID muss nicht gespeichert werden, die IDENTITY muss sein). Dies gilt auch für NEWSEQUENTIALGUID.
Sie könnten den Test fairer gestalten und einen SEQUENCER mit einem großen CACHE verwenden - das ist billiger als IDENTITY.
Aber wie MR sagt, haben GUIDs einige wesentliche Vorteile. Tatsächlich sind sie VIEL skalierbarer als IDENTITY-Spalten (aber nur, wenn sie NICHT sequenziell sind).
Siehe: http://blog.kejser.org/2011/10/05/boosting-insert-speed-by-generating-scalable-keys/
quelle
IDENTITY
. daher hier BeschwerdenDiese Art von Frage fasziniert mich. Warum musstest du es an einem Freitagabend posten? :)
Ich denke, auch wenn Ihr Test NUR zur Messung der INSERT-Leistung gedacht ist, haben Sie (möglicherweise) eine Reihe von Faktoren eingeführt, die irreführend sein könnten (Schleifen, eine lang andauernde Transaktion usw.).
Ich bin nicht ganz davon überzeugt, dass meine Version irgendetwas beweist, aber Identität funktioniert besser als die darin enthaltenen GUIDs (3,2 Sekunden gegenüber 6,8 Sekunden auf einem Heim-PC):
quelle
Ich habe Ihr Beispielskript mehrmals ausgeführt und einige Änderungen an der Anzahl und Größe der Stapel vorgenommen (und vielen Dank, dass Sie es bereitgestellt haben).
Zuerst möchte ich sagen, dass Sie nur einen Aspekt der Leistung der Tasten messen - die
INSERT
Geschwindigkeit. Wenn Sie sich also nicht speziell darum kümmern, Daten so schnell wie möglich in die Tabellen zu bekommen, steckt viel mehr in diesem Tier.Meine Ergebnisse waren im Allgemeinen ähnlich wie bei Ihnen. Allerdings würde ich diese Varianz erwähnt
INSERT
Geschwindigkeit zwischenGUID
undIDENTITY
(int) ist etwas größer mitGUID
als mitIDENTITY
- vielleicht +/- 10% zwischen den Läufen. Die verwendeten ChargenIDENTITY
variierten jeweils um weniger als 2 - 3%.Zu beachten ist auch, dass meine Testbox deutlich weniger leistungsstark ist als Ihre, sodass ich kleinere Zeilenzahlen verwenden musste.
quelle
Ich werde für dasselbe Thema auf eine andere Conv zum Stackoverflow zurückgreifen - https://stackoverflow.com/questions/170346/was-ist-die-Leistungsverbesserung-der-Sequenz-Guid-über-Standard-Guid
Ich weiß, dass bei sequenziellen GUIDs die Indexnutzung aufgrund der geringen Blattbewegung besser ist und daher die HD-Suche verringert wird. Ich würde daher denken, dass die Einfügungen auch schneller sind, da sie die Schlüssel nicht über eine große Anzahl von Seiten verteilen müssen.
Nach meiner persönlichen Erfahrung ist es bei der Implementierung einer großen Datenbank mit hohem Datenverkehr besser, GUIDs zu verwenden, da diese für die Integration in andere Systeme viel skalierbarer sind. Das gilt insbesondere für die Replikation und die int / bigint-Grenzen. Nicht, dass Ihnen die bigints ausgehen würden, aber irgendwann werden Sie und fahren zurück.
quelle