Sparsing
Bei einigen Tests mit spärlichen Spalten gab es wie bei Ihnen einen Leistungsabfall, dessen direkte Ursache ich gerne kennen würde.
DDL
Ich habe zwei identische Tabellen erstellt, eine mit 4 spärlichen Spalten und eine ohne spärliche Spalten.
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
DML
Ich habe dann ungefähr 2540 NON-NULL- Werte in beide eingefügt .
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Danach habe ich 1M NULL- Werte in beide Tabellen eingefügt
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Abfragen
Nicht sparsame Tabellenausführung
Wenn Sie diese Abfrage zweimal für die neu erstellte nicht sparsame Tabelle ausführen:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
Die logischen Lesevorgänge zeigen 5257 Seiten
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Und die CPU-Zeit liegt bei 343 ms
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
spärliche Tabellenausführung
Ausführen derselben Abfrage zweimal in der Tabelle mit geringer Dichte:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
Die Lesungen sind niedriger, 1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Die CPU-Zeit ist jedoch mit 547 ms höher .
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Ausführungsplan für nicht spärliche Tabellen
Fragen
Ursprüngliche Frage
Könnte die Erhöhung der CPU-Zeit auf die Rückgabe der NULL- Werte als Ergebnismenge zurückzuführen sein, da die NULL- Werte nicht direkt in den spärlichen Spalten gespeichert werden ? Oder ist es einfach das in der Dokumentation angegebene Verhalten ?
Durch spärliche Spalten wird der Platzbedarf für Nullwerte auf Kosten eines höheren Overheads zum Abrufen von Nicht-Null-Werten reduziert
Oder bezieht sich der Overhead nur auf Lese- und Speicherbedarf?
Selbst wenn ssms mit den Verwerfungsergebnissen nach der Ausführungsoption ausgeführt wird, war die CPU-Zeit der Sparse-Auswahl höher (407 ms) als die nicht-Sparse-Auswahl (219 ms).
BEARBEITEN
Es könnte der Overhead der Nicht-Null-Werte gewesen sein, selbst wenn nur 2540 vorhanden sind, aber ich bin immer noch nicht überzeugt.
Dies scheint ungefähr die gleiche Leistung zu sein, aber der spärliche Faktor ging verloren.
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Scheint ungefähr die gleiche Ausführungszeit zu haben:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
Aber warum sind die logischen Lesevorgänge jetzt gleich hoch? Sollte der gefilterte Index für die Spalte mit geringer Dichte nicht etwas anderes als das enthaltene ID-Feld und einige andere Nicht-Datenseiten speichern?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
Und die Größe beider Indizes:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Warum sind diese gleich groß? War die Spärlichkeit verloren?
Beide Abfragepläne bei Verwendung des gefilterten Index
Zusatzinformation
select @@version
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) - 14.0.3223.3 (X64) 12. Juli 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-Bit) unter Windows Server 2012 R2 Datacenter 6.3 (Build) 9600 :) (Hypervisor)
Während Sie die Abfragen ausführen und nur das ID- Feld auswählen , ist die CPU-Zeit vergleichbar, mit niedrigeren logischen Lesevorgängen für die Sparse-Tabelle.
Größe der Tabellen
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
Beim Erzwingen des Clustered- oder Nonclustered-Index bleibt die CPU-Zeitdifferenz bestehen.
quelle
Antworten:
Scheint so. Der in der Dokumentation erwähnte "Overhead" scheint CPU-Overhead zu sein.
Bei der Profilerstellung der beiden Abfragen wurden 367 ms CPU abgetastet, während die nicht spärliche Abfrage 284 ms CPU hatte. Das ist ein Unterschied von 83 ms.
Wo ist das meiste davon?
Beide Profile sehen sich sehr ähnlich, bis sie dazu kommen
sqlmin!IndexDataSetSession::GetNextRowValuesInternal
. Zu diesem Zeitpunkt durchläuft der Sparse-Code einen Pfadsqlmin!IndexDataSetSession::GetDataLong
, der ausgeführt wird. Dabei werden einige Funktionen aufgerufen, die so aussehen, als würden sie sich auf das Sparse-Spalten-Feature (HasSparseVector
,StoreColumnValue
) beziehen , und es werden (42 + 11 =) 53 ms addiert.Ja, es scheint, dass die Optimierung des Sparse-Speichers nicht auf nicht gruppierte Indizes übertragen wird, wenn die Sparse-Spalte als Indexschlüssel verwendet wird. Nicht gruppierte Indexschlüsselspalten nehmen unabhängig von der Spärlichkeit ihre volle Größe ein, eingeschlossene Spalten belegen jedoch keinen Speicherplatz, wenn sie spärlich und NULL sind.
Wenn
DBCC PAGE
ich mir die Ausgabe einer gruppierten Indexseite mit spärlichen Spalten mit NULL-Werten ansehe, sehe ich, dass die Datensatzlänge 11 beträgt (4 für die ID + 7 für den Standard-Overhead pro Datensatz):Für den gefilterten Index ist der Datensatz immer 40, was die Summe der Größe aller Schlüsselspalten ist (4-Byte-ID + 20-Byte-Charval + 4-Byte-Varcharval + 4-Byte-Intval + 8-Byte-Big-Intval = 40 Byte).
Enthält aus irgendeinem Grund
DBCC PAGE
nicht den 7-Byte-Overhead in "Datensatzgröße" für Indexdatensätze:Die nicht gefilterte Indexgröße ist kleiner (4-Byte-ID + 4-Byte-Intval + 4-Byte-Varcharval = 12 Byte), da zwei der spärlichen Spalten enthaltene Spalten sind, wodurch wiederum die Spärlichkeitsoptimierung erzielt wird:
Ich denke, dieser Unterschied im Verhalten stimmt mit einer der Einschränkungen überein, die auf der Dokumentseite aufgeführt sind:
Sie dürfen Schlüssel in nicht gruppierten Indizes sein, aber sie werden nicht sparsam gespeichert.
quelle