Continuando la mia serie di articoli sui latch, questa volta parlerò del latch DBCC_OBJECT_METADATA e mostrerò come può essere un importante collo di bottiglia per i controlli di coerenza prima di SQL Server 2016 in determinate circostanze. Il problema riguarda DBCC CHECKDB, DBCC CHECKTABLE e DBCC CHECKFILEGROUP, ma per chiarezza mi limiterò a fare riferimento a DBCC CHECKDB per il resto di questo post.
Potresti chiederti perché sto scrivendo di un problema che riguarda le versioni precedenti, ma esiste ancora un numero enorme di istanze di SQL Server 2014 e precedenti, quindi è un argomento valido per la mia serie.
Ti consiglio vivamente di leggere il post iniziale della serie prima di questo, in modo da avere tutte le conoscenze di base generali sui fermi.
Che cos'è il fermo DBCC_OBJECT_METADATA?
Per spiegare questo latch, ho bisogno di spiegare un po' come funziona DBCC CHECKDB.
Tra l'enorme numero di controlli di coerenza eseguiti da DBCC CHECKDB c'è un controllo della correttezza degli indici non cluster. In particolare, DBCC CHECKDB assicura:
- Per ogni record di indice non cluster in ogni indice non cluster, esiste esattamente un record di dati "corrispondente" nella tabella di base (un heap o un indice cluster)
- Per ogni record di dati in una tabella, esiste esattamente un record di indice non cluster "corrispondente" in ogni indice non cluster definito per la tabella, tenendo conto degli indici filtrati
Senza entrare troppo in dettaglio nei dettagli di come questo viene fatto, per ogni record di dati in una tabella, DBCC CHECKDB costruisce ogni record di indice non cluster che dovrebbe esistere per ogni indice non cluster e si assicura che il record di indice non cluster costruito corrisponda esattamente a quello effettivo record di indice non cluster. Se l'indice non cluster contiene una colonna calcolata (come parte della chiave dell'indice non cluster o come colonna INCLUDEd), DBCC CHECKDB deve calcolare il valore della colonna calcolata da utilizzare durante la costruzione dei record dell'indice.
Oltre ai controlli di correttezza dell'indice non cluster, se è presente un persistente colonna calcolata nella definizione di una tabella, quindi per ogni record di dati nella tabella, DBCC CHECKDB deve verificare che il valore persistente sia corretto, indipendentemente dal fatto che quella colonna faccia parte o meno di un indice non cluster.
Quindi, come fa a capire i valori di colonna calcolati?
Il Query Processor fornisce un meccanismo per calcolare i valori di colonna calcolati, chiamato "valutatore di espressioni". DBCC CHECKDB chiama tale funzione, fornendo informazioni sui metadati appropriati e il record di dati e il valutatore di espressioni utilizza la definizione archiviata della colonna calcolata nei metadati e i valori del record di dati e restituisce il valore della colonna calcolata per l'utilizzo da parte di DBCC CHECKDB . Il funzionamento interno del valutatore di espressioni è al di fuori del controllo del codice DBCC, ma per poter utilizzare il valutatore di espressioni, è necessario prima acquisire un latch; il fermo DBCC_OBJECT_METADATA.
In che modo il fermo diventa un collo di bottiglia?
Ecco il problema:esiste solo una modalità accettabile in cui è possibile acquisire il latch DBCC_OBJECT_METADATA prima di utilizzare il valutatore di espressioni, e questa è la modalità EX (esclusiva). E come saprai leggendo il post introduttivo alla serie, solo un thread alla volta può tenere il fermo in modalità EX.
Raccolta di tutte queste informazioni:quando un database ha colonne calcolate persistenti o indici non cluster che contengono colonne calcolate, è necessario utilizzare il valutatore di espressioni. Se l'edizione di SQL Server è Enterprise, DBCC CHECKDB è in grado di utilizzare il parallelismo e quindi ha più thread che eseguono i vari controlli. E non appena hai più thread che cercano di acquisire un latch in modalità EX, quel latch diventa un collo di bottiglia. Quanto grande diventa un collo di bottiglia dipende da quanto deve essere utilizzato il valutatore di espressioni, quindi più colonne calcolate persistenti o indici non cluster che utilizzano colonne calcolate ci sono e maggiore è il numero di righe di tabella in quelle tabelle, il maggiore è il collo di bottiglia che diventa il latch DBCC _OBJECT_METADATA.
Ma ricorda, questo collo di bottiglia si verifica solo per le versioni di SQL Server precedenti a SQL Server 2016. In SQL Server 2016 Microsoft ha deciso di "aggiustare" il collo di bottiglia disattivando i controlli degli indici non cluster utilizzando colonne calcolate per impostazione predefinita e facendoli solo quando il WITH Viene utilizzata l'opzione EXTENDED_LOGICAL_CHECKS.
Mostra il collo di bottiglia
Puoi riprodurre facilmente il collo di bottiglia eseguendo DBCC CHECKDB su un database che ha colonne calcolate persistenti o indici non cluster con colonne calcolate e il database AdventureWorks fornito da Microsoft è un ottimo esempio. Puoi scaricare i backup di AdventureWorks per la tua versione di SQL Server da qui. Ho eseguito alcuni test utilizzando un database AdventureWorks2014 su un'istanza SQL Server 2014 (su un Dell R720 a 32 core) e ho ampliato il database a poche centinaia di GB utilizzando gli script di Jonathan.
Quando ho eseguito DBCC CHECKDB, con il server MAXDOP impostato su 0, l'esecuzione ha richiesto più di 5 ore. Il tipo di attesa LATCH_EX rappresentava circa il 30% delle attese, con ogni attesa di appena 1 millisecondo e il 99% delle attese LATCH_EX riguardava il latch DBCC_OBJECT_METADATA.
Ho cercato indici non cluster contenenti colonne calcolate utilizzando il codice seguente:
SELECT [s].[name] AS [Schema], [o].[name] AS [Object], [i].[name] AS [Index], [c].[name] AS [Column], [ic].* FROM sys.columns [c] JOIN sys.index_columns [ic] ON [ic].[object_id] = [c].[object_id] AND [ic].[column_id] = [c].[column_id] JOIN sys.indexes [i] ON [i].[object_id] = [ic].[object_id] AND [i].[index_id] = [ic].[index_id] JOIN sys.objects [o] ON [i].[object_id] = [o].[object_id] JOIN sys.schemas [s] ON [o].[schema_id] = [s].[schema_id] WHERE [c].[is_computed] = 1;
Quel codice ha trovato sei indici non cluster nel database AdventureWorks2014. Ho disabilitato tutti e sei gli indici (usando ALTER INDEX … DISABLE) e ho eseguito nuovamente DBCC CHECKDB ed è stato completato in circa 18 minuti. Quindi il collo di bottiglia del latch DBCC_OBJECT_METADATA è stato un fattore importante nel far funzionare DBCC CHECKDB più di 16 volte più lentamente!
Riepilogo
Sfortunatamente, la disabilitazione degli indici non cluster utilizzando le colonne calcolate (e quindi riabilitarli in seguito utilizzando ALTER INDEX … REBUILD) è l' *unico * modo per rimuovere il collo di bottiglia del latch DBCC_OBJECT_METADATA nelle versioni precedenti a SQL Server 2016 pur mantenendo tutte le altre funzionalità di DBCC CHECKDB. La disabilitazione degli indici non cluster probabilmente non è qualcosa che vorrai fare in un ambiente di produzione a meno che tu non abbia una finestra di manutenzione con attività zero. Ciò significa che probabilmente disabiliterai quegli indici non cluster per rimuovere il collo di bottiglia solo se i tuoi controlli di coerenza vengono scaricati su un altro server utilizzando il metodo backup-copy-restore-CHECKDB.
Un altro modo per farlo è utilizzare l'opzione WITH PHYSICAL_ONLY durante l'esecuzione di DBCC CHECKDB, ma poi perdi tutti i controlli logici approfonditi, quindi non sono un grande fan di consigliarlo come soluzione.