Perché è necessario più tempo per creare un indice dopo l'aumento delle dimensioni della colonna?


18

Il nostro fornitore ha modificato la larghezza delle colonne in quasi tutte le colonne dell'intero database. Il database è di circa 7 TB, oltre 9000 tabelle. Stiamo cercando di creare un indice su una tabella con 5,5 miliardi di righe. Prima dell'aggiornamento del fornitore, potremmo creare l'indice in 2 ore. Adesso ci vogliono giorni. Quello che hanno fatto è aumentare qualsiasi dimensione varchar (xx) a varchar (256). Quindi la maggior parte delle colonne erano varchar (18) o varchar (75), ecc.

Comunque la chiave primaria è composta da 6 colonne che hanno combinato la larghezza di 126 caratteri. Ora dopo l'aggiornamento, la chiave primaria è di 1283 caratteri che viola il limite dei server SQL di 900 caratteri. L'intera larghezza della colonna della tabella è passata da un conteggio varchar totale combinato di 1049 a un conteggio varchar totale combinato di 4009.

Non c'è un aumento dei dati, la tabella non occupa più "spazio" di quanto non facesse prima che aumentasse tutta la larghezza della colonna, ma le prestazioni per creare qualcosa di così semplice come un indice ora richiedono un irragionevole tempo.

Qualcuno può spiegare perché ci vuole così tanto più tempo per creare e indicizzare quando l'unica cosa fatta era aumentare le dimensioni delle colonne?

L'indice che stiamo cercando di creare non è cluster poiché il pk è l'indice cluster. Dopo diversi tentativi di creare l'indice, ci siamo arresi. Penso che abbia funzionato 4 o 5 giorni senza completamento.

Ho provato questo in un ambiente non di produzione prendendo un'istantanea del file system e ho portato il database su un server più silenzioso.

Risposte:


12

Remus ha utilmente sottolineato che la lunghezza massima della VARCHARcolonna influisce sulla dimensione della riga stimata e quindi sulla memoria che fornisce SQL Server.

Ho cercato di fare un po 'più di ricerca per ampliare la parte della sua risposta "da questo su cose a cascata". Non ho una spiegazione completa o concisa, ma ecco quello che ho trovato.

Script di riproduzione

Ho creato uno script completo che genera un set di dati falso su cui la creazione dell'indice impiega circa 10 volte più tempo sul mio computer per la VARCHAR(256)versione. I dati utilizzati è esattamente lo stesso, ma la prima tabella utilizza le effettive lunghezze massime di 18, 75, 9, 15, 123, e 5, mentre tutte le colonne utilizzano una lunghezza massima di 256nella seconda tabella.


Digitare la tabella originale

Qui vediamo che la query originale si completa in circa 20 secondi e le letture logiche sono uguali alla dimensione della tabella di ~1.5GB(195K pagine, 8K per pagina).

-- CPU time = 37674 ms,  elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Digitazione della tabella VARCHAR (256)

Per la VARCHAR(256)tabella, vediamo che il tempo trascorso è aumentato drammaticamente.

È interessante notare che né il tempo della CPU né le letture logiche aumentano. Ciò ha senso dato che la tabella ha gli stessi dati esatti, ma non spiega perché il tempo trascorso sia molto più lento.

-- CPU time = 33212 ms,  elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO


Statistiche I / O e wait: originali

Se acquisiamo un po 'più di dettagli (usando p_perfMon, una procedura che ho scritto ), possiamo vedere che la maggior parte dell'I / O viene eseguita sul LOGfile. Vediamo una quantità relativamente modesta di I / O sull'effettivo ROWS(il file di dati principale) e il tipo di attesa principale è LATCH_EX, indicando una contesa di pagine in memoria.

Possiamo anche vedere che il mio disco rotante si trova tra "cattivo" e "tremendamente cattivo", secondo Paul Randal :)

inserisci qui la descrizione dell'immagine


Statistiche I / O e wait: VARCHAR (256)

Per la VARCHAR(256)versione, le statistiche I / O e wait sembrano completamente diverse! Qui vediamo un enorme aumento dell'I / O sul file di dati ( ROWS), e i tempi di stallo ora fanno di Paul Randal semplicemente dire "WOW!".

Non sorprende che il tipo di attesa n. 1 sia ora IO_COMPLETION. Ma perché viene generato così tanto I / O?

inserisci qui la descrizione dell'immagine


Piano di query effettivo: VARCHAR (256)

Dal piano di query, possiamo vedere che l' Sortoperatore ha una fuoriuscita ricorsiva (5 livelli di profondità!) Nella VARCHAR(256)versione della query. (Non c'è alcun versamento nella versione originale.)

inserisci qui la descrizione dell'immagine


Avanzamento query live: VARCHAR (256)

Possiamo usare sys.dm_exec_query_profiles per visualizzare l'avanzamento delle query live in SQL 2014+ . Nella versione originale, l'intero Table Scane Sortvengono elaborati senza versamenti ( spill_page_countrimane 0tutto).

Nella VARCHAR(256)versione, tuttavia, possiamo vedere che gli spargimenti di pagina si accumulano rapidamente per l' Sortoperatore. Ecco un'istantanea dell'avanzamento della query appena prima del completamento della query. I dati qui sono aggregati su tutti i thread.

inserisci qui la descrizione dell'immagine

Se scavo individualmente in ogni thread, vedo che 2 thread completano l'ordinamento in circa 5 secondi (@ 20 secondi in totale, dopo 15 secondi trascorsi nella scansione della tabella). Se tutti i thread VARCHAR(256)avessero progredito a questa velocità, la creazione dell'indice sarebbe stata completata all'incirca nello stesso momento della tabella originale.

Tuttavia, i restanti 6 thread procedono a una velocità molto più lenta. Ciò può essere dovuto al modo in cui la memoria viene allocata e al modo in cui i thread vengono trattenuti dall'I / O mentre si riversano dati. Non lo so per certo però.

inserisci qui la descrizione dell'immagine


Cosa sai fare?

Esistono diverse cose che potresti considerare di provare:

  • Collaborare con il fornitore per ripristinare una versione precedente. Se ciò non è possibile, lascia che il fornitore non sia soddisfatto di questa modifica in modo che possano prendere in considerazione la possibilità di ripristinarla in una versione futura.
  • Quando aggiungi il tuo indice, considera l'utilizzo di OPTION (MAXDOP X)dove Xc'è un numero inferiore rispetto all'impostazione attuale a livello di server. Quando ho usato OPTION (MAXDOP 2)questo specifico set di dati sul mio computer, la VARCHAR(256)versione è stata completata in 25 seconds(rispetto a 3-4 minuti con 8 thread!). È possibile che il comportamento di fuoriuscita sia aggravato da un parallelismo più elevato.
  • Se è possibile un investimento hardware aggiuntivo, profilare l'I / O (il probabile collo di bottiglia) sul proprio sistema e considerare l'utilizzo di un SSD per ridurre la latenza dell'I / O sostenuta dagli sversamenti.


Ulteriori letture

Paul White ha un bel post sul blog all'interno di SQL Server che potrebbe essere interessante. Parla un po 'di spargimento, inclinazione del thread e allocazione della memoria per tipi paralleli.


11

La tabella di ordinamento intermedia verrà stimata in modo diverso tra i due casi. Ciò porterà a richieste di concessione di memoria diverse ( VARCHAR(256)sarà maggiore) e probabilmente a una concessione effettiva molto più piccola, in termini percentuali, rispetto alla richiesta "ideale". Immagino che questo porti a sversamenti durante l'ordinamento.

Testando lo script da Geoff (solo su 100.000 righe) posso vedere chiaramente la differenza nella dimensione della riga stimata dell'ordinamento (141B contro 789B). Da questo in poi le cose sono in cascata.


8
Sono sicuro che Paul dimostrerà una risposta più completa e completa, forse anche con stack di chiamate, che verranno utilizzati come materiale didattico dal team di sviluppo. Ancora una volta ...
Remus Rusanu,
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.