Um Ihre Anfrage zu überprüfen, habe ich 2 Tabellen nach diesem Schema erstellt:
- 7,9 Millionen Datensätze mit Informationen zum Kontostand.
- ein Identitätsfeld von 1 bis 7,9 Millionen
- Ein Zahlenfeld, das die Datensätze in ca. 500.000 Gruppen gruppiert.
Die erste aufgerufene Tabelle heap
hat einen nicht gruppierten Index für das Feld group
. Die zweite aufgerufene Tabelle clust
hat einen Clustered-Index für das aufgerufene sequentielle Feld key
und einen Nonclustered-Index für das Feldgroup
Die Tests wurden auf einem I5 M540-Prozessor mit 2 Hyperthread-Kernen, 4 GB Speicher und 64-Bit-Fenstern 7 durchgeführt.
Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64)
Apr 2 2010 15:48:46
Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
Update am 9. März 2011 : Ich habe einen zweiten umfassenderen Benchmark durchgeführt, indem ich den folgenden .net-Code und die Protokollierungsdauer, die CPU, die Lese-, Schreib- und Zeilenanzahl in SQL Server Profiler ausgeführt habe. (Der verwendete CommandText wird in den Ergebnissen erwähnt.)
HINWEIS: CPU und Dauer werden in Millisekunden angegeben
- 1000 Abfragen
- CPU-Anfragen von Null werden aus den Ergebnissen eliminiert
- 0 betroffene Zeilen werden aus den Ergebnissen entfernt
int[] idList = new int[] { 6816588, 7086702, 6498815 ... }; // 1000 values here.
using (var conn = new SqlConnection(@"Data Source=myserver;Initial Catalog=mydb;Integrated Security=SSPI;"))
{
conn.Open();
using (var cmd = new SqlCommand())
{
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "select * from heap where common_key between @id and @id+1000";
cmd.Parameters.Add("@id", SqlDbType.Int);
cmd.Prepare();
foreach (int id in idList)
{
cmd.Parameters[0].Value = id;
using (var reader = cmd.ExecuteReader())
{
int count = 0;
while (reader.Read())
{
count++;
}
Console.WriteLine(String.Format("key: {0} => {1} rows", id, count));
}
}
}
}
Ende der Aktualisierung am 9. März 2011 .
SELECT Leistung
Um die Performance-Zahlen zu überprüfen, habe ich die folgenden Abfragen einmal in der Heap-Tabelle und einmal in der Clust-Tabelle durchgeführt:
select * from heap/clust where group between 5678910 and 5679410
select * from heap/clust where group between 6234567 and 6234967
select * from heap/clust where group between 6455429 and 6455729
select * from heap/clust where group between 6655429 and 6655729
select * from heap/clust where group between 6955429 and 6955729
select * from heap/clust where group between 7195542 and 7155729
Die Ergebnisse dieses Benchmarks beziehen sich auf heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 1510 31ms 309ms
401 405 15ms 283ms
2700 2709 0ms 472ms
0 3 0ms 30ms
2953 2962 32ms 257ms
0 0 0ms 0ms
Update am 9. März 2011 :
cmd.CommandText = "select * from heap where group between @id and @id+1000";
- 721 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 6368 -
Cpu 15 374 37 0.00754
Reads 1069 91459 7682 1.20155
Writes 0 0 0 0.00000
Duration 0.3716 282.4850 10.3672 0.00180
Ende der Aktualisierung am 9. März 2011 .
Für die Tabelle sind clust
die Ergebnisse:
rows reads CPU Elapsed
----- ----- ----- --------
1503 4827 31ms 327ms
401 1241 0ms 242ms
2700 8372 0ms 410ms
0 3 0ms 0ms
2953 9060 47ms 213ms
0 0 0ms 0ms
Update am 9. März 2011 :
cmd.CommandText = "select * from clust where group between @id and @id+1000";
- 721 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 6056 -
Cpu 15 468 38 0.00782
Reads 3194 227018 20457 3.37618
Writes 0 0 0 0.0
Duration 0.3949 159.6223 11.5699 0.00214
Ende der Aktualisierung am 9. März 2011 .
SELECT WITH JOIN-Leistung
cmd.CommandText = "select * from heap/clust h join keys k on h.group = k.group where h.group between @id and @id+1000";
Die Ergebnisse dieses Benchmarks beziehen sich auf heap
:
873 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1009 4170 1683 -
Cpu 15 47 18 0.01175
Reads 2145 5518 2867 1.79246
Writes 0 0 0 0.00000
Duration 0.8215 131.9583 1.9095 0.00123
Die Ergebnisse dieses Benchmarks beziehen sich auf clust
:
865 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 4143 1685 -
Cpu 15 47 18 0.01193
Reads 5320 18690 8237 4.97813
Writes 0 0 0 0.00000
Duration 0.9699 20.3217 1.7934 0.00109
UPDATE-Leistung
Die zweite Gruppe von Abfragen sind Aktualisierungsanweisungen:
update heap/clust set amount = amount + 0 where group between 5678910 and 5679410
update heap/clust set amount = amount + 0 where group between 6234567 and 6234967
update heap/clust set amount = amount + 0 where group between 6455429 and 6455729
update heap/clust set amount = amount + 0 where group between 6655429 and 6655729
update heap/clust set amount = amount + 0 where group between 6955429 and 6955729
update heap/clust set amount = amount + 0 where group between 7195542 and 7155729
Die Ergebnisse dieses Benchmarks für heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 3013 31ms 175ms
401 806 0ms 22ms
2700 5409 47ms 100ms
0 3 0ms 0ms
2953 5915 31ms 88ms
0 0 0ms 0ms
Update am 9. März 2011 :
cmd.CommandText = "update heap set amount = amount + @id where group between @id and @id+1000";
- 811 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 5598 811
Cpu 15 873 56 0.01199
Reads 2080 167593 11809 2.11217
Writes 0 1687 121 0.02170
Duration 0.6705 514.5347 17.2041 0.00344
Ende der Aktualisierung am 9. März 2011 .
Die Ergebnisse dieses Benchmarks für clust
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 9126 16ms 35ms
401 2444 0ms 4ms
2700 16385 31ms 54ms
0 3 0ms 0ms
2953 17919 31ms 35ms
0 0 0ms 0ms
Update am 9. März 2011 :
cmd.CommandText = "update clust set amount = amount + @id where group between @id and @id+1000";
- 853 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1001 69788 5420 -
Cpu 15 594 50 0.01073
Reads 6226 432237 33597 6.20450
Writes 0 1730 110 0.01971
Duration 0.9134 193.7685 8.2919 0.00155
Ende der Aktualisierung am 9. März 2011 .
LÖSCHEN Benchmarks
Die dritte Gruppe von Abfragen, die ich ausgeführt habe, sind delete-Anweisungen
delete heap/clust where group between 5678910 and 5679410
delete heap/clust where group between 6234567 and 6234967
delete heap/clust where group between 6455429 and 6455729
delete heap/clust where group between 6655429 and 6655729
delete heap/clust where group between 6955429 and 6955729
delete heap/clust where group between 7195542 and 7155729
Das Ergebnis dieses Benchmarks für die heap
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 10630 62ms 179ms
401 2838 0ms 26ms
2700 19077 47ms 87ms
0 4 0ms 0ms
2953 20865 62ms 196ms
0 4 0ms 9ms
Update am 9. März 2011 :
cmd.CommandText = "delete heap where group between @id and @id+1000";
- 724 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 192 69788 4781 -
Cpu 15 499 45 0.01247
Reads 841 307958 20987 4.37880
Writes 2 1819 127 0.02648
Duration 0.3775 1534.3383 17.2412 0.00349
Ende der Aktualisierung am 9. März 2011 .
das Ergebnis dieser Benchmark für die clust
:
rows reads CPU Elapsed
----- ----- ----- --------
1503 9228 16ms 55ms
401 3681 0ms 50ms
2700 24644 46ms 79ms
0 3 0ms 0ms
2953 26955 47ms 92ms
0 3 0ms 0ms
Update am 9. März 2011 :
cmd.CommandText = "delete clust where group between @id and @id+1000";
- 751 Zeilen haben> 0 CPU und betreffen mehr als 0 Zeilen
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 144 69788 4648 -
Cpu 15 764 56 0.01538
Reads 989 458467 30207 6.48490
Writes 2 1830 127 0.02694
Duration 0.2938 2512.1968 24.3714 0.00555
Ende der Aktualisierung am 9. März 2011 .
INSERT-Benchmarks
Der letzte Teil des Benchmarks ist die Ausführung von Insert-Anweisungen.
In Heap / Clust einfügen (...) Werte (...), (...), (...), (...), (...), (...)
Das Ergebnis dieses Benchmarks für die heap
:
rows reads CPU Elapsed
----- ----- ----- --------
6 38 0ms 31ms
Update am 9. März 2011 :
string str = @"insert into heap (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
values";
for (int x = 0; x < 999; x++)
{
str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'), ", x);
}
str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);
cmd.CommandText = str;
- 912 Anweisungen haben> 0 CPU
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 1000 1000 -
Cpu 15 2138 25 0.02500
Reads 5212 7069 6328 6.32837
Writes 16 34 22 0.02222
Duration 1.6336 293.2132 4.4009 0.00440
Ende der Aktualisierung am 9. März 2011 .
Das Ergebnis dieses Benchmarks für die clust
:
rows reads CPU Elapsed
----- ----- ----- --------
6 50 0ms 18ms
Update am 9. März 2011 :
string str = @"insert into clust (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
values";
for (int x = 0; x < 999; x++)
{
str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'), ", x);
}
str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);
cmd.CommandText = str;
- 946 Anweisungen haben> 0 CPU
Counter Minimum Maximum Average Weighted
--------- ------- ---------- ------- ---------
RowCounts 1000 1000 1000 -
Cpu 15 2403 21 0.02157
Reads 6810 8997 8412 8.41223
Writes 16 25 19 0.01942
Duration 1.5375 268.2571 6.1463 0.00614
Ende der Aktualisierung am 9. März 2011 .
Schlussfolgerungen
Obwohl beim Zugriff auf die Tabelle mit dem gruppierten und dem nicht gruppierten Index mehr logische Lesevorgänge stattfinden (während der nicht gruppierte Index verwendet wird), sind die Leistungsergebnisse:
- SELECT-Anweisungen sind vergleichbar
- UPDATE-Anweisungen sind schneller, wenn ein Clustered-Index vorhanden ist
- DELETE-Anweisungen sind schneller, wenn ein Clustered-Index vorhanden ist
- INSERT-Anweisungen sind schneller, wenn ein Clustered-Index vorhanden ist
Natürlich war mein Benchmark für eine bestimmte Art von Tabelle und mit einer sehr begrenzten Anzahl von Abfragen sehr begrenzt, aber ich denke, dass wir aufgrund dieser Informationen bereits sagen können, dass es praktisch immer besser ist, einen Clustered-Index für Ihre Tabelle zu erstellen.
Update am 9. März 2011 :
Wie wir aus den hinzugefügten Ergebnissen ersehen können, waren die Schlussfolgerungen zu den begrenzten Tests nicht in jedem Fall korrekt.
Die Ergebnisse zeigen nun, dass die einzigen Anweisungen, die vom Clustered-Index profitieren, die Update-Anweisungen sind. Die anderen Aussagen sind in der Tabelle mit gruppiertem Index um 30% langsamer.
Einige zusätzliche Diagramme, in denen ich die gewichtete Dauer pro Abfrage für Heap vs. Clust aufgezeichnet habe.
Wie Sie sehen, ist das Leistungsprofil für die insert-Anweisungen sehr interessant. Die Spitzen werden durch einige Datenpunkte verursacht, deren Fertigstellung viel länger dauert.
Ende der Aktualisierung am 9. März 2011 .
Wie Kimberly Tripp - die Königin der Indizierung - in ihrem Blogbeitrag The Clustered Index Debate (Die Debatte um den Clustered Index) recht gut erklärt, beschleunigt das Vorhandensein eines Clustering-Schlüssels in einer Datenbanktabelle alle Vorgänge - nicht nur
SELECT
.SELECT ist auf einem Heap im Vergleich zu einer gruppierten Tabelle im Allgemeinen langsamer, solange Sie einen guten Clustering - Schlüssel auswählen - so etwas wie eine
INT IDENTITY
. Wenn Sie einen wirklich sehr, sehr schlechten Clustering-Schlüssel wie eine GUID oder einen zusammengesetzten Schlüssel mit vielen Komponenten variabler Länge verwenden, ist ein Heap möglicherweise schneller. Aber in diesem Fall müssen Sie Ihr Datenbankdesign unbedingt bereinigen ...Im Allgemeinen glaube ich nicht, dass ein Haufen Sinn macht - wählen Sie einen guten, nützlichen Clustering-Schlüssel und Sie sollten in jeder Hinsicht davon profitieren.
quelle
Ich bin gerade auf diesen Artikel von Joe Chang gestoßen, der diese Frage anspricht. Fügte seine Schlussfolgerungen unten ein.
quelle