Colonne sparse, tempo della cpu e indici filtrati


10

Sparsing

Durante l'esecuzione di alcuni test su colonne sparse, si è verificato un calo delle prestazioni di cui vorrei conoscere la causa diretta.

DDL

Ho creato due tabelle identiche, una con 4 colonne sparse e una senza colonne sparse.

--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

Ho quindi inserito circa 2540 valori NON NULL in entrambi.

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;

Successivamente, ho inserito i valori NULL 1M in entrambe le tabelle

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;

Interrogazioni

Esecuzione della tabella non parsimoniosa

Quando si esegue questa query due volte sulla tabella non sparse appena creata:

SET STATISTICS IO, TIME ON;
SELECT  * FROM dbo.nonsparse
WHERE   1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);

Le letture logiche mostrano 5257 pagine

(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.

E il tempo della CPU è di 343 ms

 SQL Server Execution Times:
   CPU time = 343 ms,  elapsed time = 3850 ms.

esecuzione di tabelle sparse

Esecuzione della stessa query due volte sulla tabella sparsa:

SELECT  * FROM dbo.sparse
WHERE   1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);

Le letture sono più basse, 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.

Ma il tempo della CPU è più alto, 547 ms .

 SQL Server Execution Times:
   CPU time = 547 ms,  elapsed time = 2406 ms.

Piano di esecuzione delle tabelle sparse

piano di esecuzione della tabella non sparsa


Domande

Domanda originale

Dato che i valori NULL non sono memorizzati direttamente nelle colonne sparse, l'aumento del tempo della CPU potrebbe essere dovuto alla restituzione dei valori NULL come set di risultati? O è semplicemente il comportamento come indicato nella documentazione ?

Le colonne sparse riducono i requisiti di spazio per i valori null a costo di un overhead per recuperare valori non null

Oppure l'overhead è correlato solo alle letture e all'archiviazione utilizzate?

Anche quando si eseguono gli sms con i risultati di scarto dopo l'esecuzione, il tempo della CPU della selezione sparsa era maggiore (407 ms) rispetto al non sparse (219 ms).

MODIFICARE

Potrebbe essere stato il sovraccarico dei valori non nulli, anche se sono presenti solo 2540, ma non sono ancora convinto.

Questo sembra avere le stesse prestazioni, ma il fattore scarso è stato perso.

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);

Sembra avere circa lo stesso tempo di esecuzione:

 SQL Server Execution Times:
   CPU time = 297 ms,  elapsed time = 292 ms.

 SQL Server Execution Times:
   CPU time = 281 ms,  elapsed time = 319 ms.

Ma perché le letture logiche ora hanno lo stesso importo? L'indice filtrato per la colonna sparsa non dovrebbe archiviare nulla tranne il campo ID incluso e alcune altre pagine non di dati?

Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785

E la dimensione di entrambi gli indici:

RowCounts   Used_MB Unused_MB   Total_MB
1000000     45.20   0.06        45.26

Perché queste hanno le stesse dimensioni? La radiosità è stata persa?

Entrambi i piani di query quando si utilizza l'indice filtrato


Informazioni extra

select @@version

Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) - 14.0.3223.3 (X64) 12 lug 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) su Windows Server 2012 R2 Datacenter 6.3 (Build 9600:) (Hypervisor)

Durante l'esecuzione delle query e selezionando solo il campo ID , il tempo della CPU è comparabile, con letture logiche inferiori per la tabella sparsa.

Dimensione dei tavoli

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

Quando si forza l'indice cluster o non cluster, la differenza di tempo della cpu rimane.


1
Potresti ottenere i piani per la query post-modifica?
George.Palacios,

1
@ George.Palacios li ha aggiunti :)
Randi Vertongen,

Risposte:


6

O è semplicemente il comportamento come indicato nella documentazione?

Così sembra. Il "sovraccarico" menzionato nella documentazione sembra essere un sovraccarico della CPU.

Profilando le due query, la query sparsa ha campionato 367 ms di CPU, mentre quella non sparse aveva 284 ms di CPU. Questa è una differenza di 83 ms.

screenshot di Perfview che mostra la CPU totale per il thread che ha eseguito la query

Dov'è la maggior parte di quello?

Entrambi i profili sembrano molto simili fino a quando non arrivano sqlmin!IndexDataSetSession::GetNextRowValuesInternal. A quel punto, il codice sparse scende lungo un percorso in esecuzione sqlmin!IndexDataSetSession::GetDataLong, che chiama alcune funzioni che sembrano correlate alla funzione di colonna sparsa ( HasSparseVector, StoreColumnValue) e aggiungono fino a (42 + 11 =) 53 ms.

screenshot della differenza CPU per colonna sparsa

Perché queste hanno le stesse dimensioni? La radiosità è stata persa?

Sì, sembra che l'ottimizzazione dell'archiviazione sparsa non venga trasferita agli indici non cluster quando la colonna sparsa viene utilizzata come chiave di indice. Pertanto, le colonne chiave di indice non cluster occupano la loro dimensione completa indipendentemente dalla scarsità, ma le colonne incluse occupano spazio zero se sono sparse e NULL.

Osservando l' DBCC PAGEoutput da una pagina di indice cluster con colonne sparse con valore NULL, posso vedere che la lunghezza del record è 11 (4 per l'ID + 7 per l'overhead standard per record):

Record Type = PRIMARY_RECORD        Record Attributes =  NULL_BITMAP    Record Size = 11

Per l'indice filtrato, il record è sempre 40, che è la somma della dimensione di tutte le colonne chiave (ID 4 byte + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 byte).

Per qualche motivo, DBCC PAGEnon include l'overhead di 7 byte in "Dimensioni record" per i record indice:

Record Type = INDEX_RECORD          Record Attributes =  NULL_BITMAP    Record Size = 40

La dimensione dell'indice non filtrata è inferiore (4 byte ID + 4 byte intval + 4 byte varcharval = 12 byte) perché due delle colonne sparse sono incluse colonne, che ottengono nuovamente l'ottimizzazione della sparseness:

Record Type = INDEX_RECORD          Record Attributes =  NULL_BITMAP    Record Size = 12

Immagino che questa differenza di comportamento sia in linea con una delle limitazioni elencate nella pagina dei documenti:

Una colonna sparsa non può far parte di un indice cluster o di un indice chiave primaria univoco

Possono essere chiavi in ​​indici non cluster, ma non sono archiviati, a malapena.


2
Ben fatto! Grazie ancora!
Randi Vertongen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.