Per concludere la mia breve serie di articoli sui latch, questa volta parlerò di alcuni altri latch in SQL Server che potresti vedere occasionalmente ma non meritano un articolo completo da soli. Come al solito, ti consiglio vivamente di leggere il post iniziale della serie prima di questo, in modo da avere tutte le conoscenze generali di base sui latch.
Il fermo LOG_MANAGER
Il latch LOG_MANAGER viene utilizzato per la sincronizzazione durante alcune operazioni che coinvolgono il log delle transazioni ed è presente un latch LOG_MANAGER per database (poiché ogni database ha il proprio log manager). Può essere acquisito solo in modalità esclusiva e può costituire un collo di bottiglia durante la crescita del file di registro delle transazioni. Lo scenario in cui diventerà evidente come un problema è:
- Il file di registro ha una piccola crescita automatica impostata
- Esistono molte connessioni simultanee che generano record di log delle transazioni
- Il file di registro continua a dover crescere
Quando il file di registro esaurisce lo spazio, dovrà crescere. Il primo thread per realizzare più spazio di log è necessario acquisisce il latch LOG_MANAGER in modalità EX e procede a far crescere il file di log. Molti altri thread continuano a provare a generare record di registro e ad entrare nella coda per il latch LOG_MANAGER, in modo che possano aumentare il file di registro. Quando il primo thread rilascia il fermo, il successivo lo prende e si rende conto che il registro è già cresciuto, quindi lo lascia cadere e continua. E così via. Per inciso, questo modello di collo di bottiglia è chiamato convoglio latch .
Puoi pensarlo esattamente come lo stesso collo di bottiglia del latch FGCB_ADD_REMOVE di cui ho discusso in precedenza nella serie, ma con la crescita del file di registro anziché la crescita del file di dati. Tuttavia, con il latch FGCB_ADD_REMOVE, di solito l'istanza ha l'inizializzazione del file istantanea abilitata, quindi la crescita del file è molto veloce, ma con il latch LOG_MANAGER, il registro *deve* essere inizializzato a zero e il tempo perso nella coda del latch è più lungo .
La soluzione per questo collo di bottiglia si compone di tre parti:
- Imposta correttamente la crescita automatica del file di registro, in modo che il registro non cresca frequentemente
- Ridimensiona il log correttamente per il carico di lavoro, quindi il log non dovrebbe crescere affatto
- Assicurati che il registro venga cancellato correttamente, in modo che il registro non debba crescere
Se questi sono tutti a posto, non dovresti vedere il fermo LOG_MANAGER come un normale collo di bottiglia e ne parlo di più nel mio post qui.
Il fermo ACCESS_METHODS_DATASET_PARENT
Quando si accede a un heap oa un indice, internamente è presente un oggetto chiamato rispettivamente HeapDataSetSession o IndexDataSetSession. Quando viene eseguita una scansione parallela, i thread che eseguono il lavoro effettivo della scansione hanno ciascuno un set di dati "figlio" (un'altra istanza dei due oggetti che ho appena descritto) e il set di dati principale, che sta davvero controllando la scansione, viene chiamato il "genitore".
Quando uno dei thread di lavoro di scansione ha esaurito il set di righe che dovrebbe scansionare, deve ottenere un nuovo intervallo accedendo al set di dati padre, il che significa acquisire il latch ACCESS_METHODS_DATASET_PARENT in modalità esclusiva. Anche se può sembrare un collo di bottiglia, in realtà non lo è e non c'è nulla che tu possa fare per impedire ai thread che eseguono una scansione parallela di mostrare occasionalmente un LATCH_EX in attesa di questo latch.
La domanda che dovresti porti è:questa query dovrebbe eseguire una scansione parallela in primo luogo? È del tutto possibile che sia successo qualcosa che ha costretto il piano di query a includere una scansione parallela quando questo potrebbe non essere il modo più efficiente per l'esecuzione della query. Esempi di cose che potrebbero causare il passaggio di un piano a una scansione parallela includono:
- Statistiche non aggiornate
- Un indice non cluster mancante o eliminato
- Nuovo codice che forza una scansione a causa di una conversione implicita:una mancata corrispondenza del tipo di dati tra una colonna e una variabile/parametro, che preclude l'uso di un indice non cluster
- Nuovo codice che forza una scansione perché l'aritmetica viene eseguita su una colonna della tabella anziché su una variabile/parametro, il che preclude nuovamente l'uso di un indice non cluster
- La crescita dei dati che si verifica e una scansione sono davvero il piano più efficiente
Oppure potrebbe essere che questa query richieda una scansione, nel qual caso LATCH_EX attende che ACCESS_METHODS_DATASET_PARENT facciano solo parte del tuo ambiente.
Il fermo ACCESS_METHODS_HOBT_VIRTUAL_ROOT
Ogni istanza di questo latch protegge una voce nei metadati di Storage Engine per un b-tree, in particolare l'ID pagina della pagina radice del b-tree (la pagina nella parte superiore del triangolo che generalmente consideriamo un indice) . Sto specificatamente dicendo b-tree e non indice , poiché un indice può avere più partizioni, ognuna delle quali ha un b-tree (essenzialmente una parte dell'indice generale, ma con vincoli di chiave di valore basso e valore alto).
Ogni volta che un thread deve attraversare un b-tree, deve iniziare dalla pagina principale e proseguire fino al livello foglia. Per leggere i metadati contenenti l'ID pagina della pagina radice, il thread deve acquisire il latch ACCESS_METHODS_HOBT_VIRTUAL_ROOT in modalità SH, per assicurarsi che l'ID pagina non sia in fase di modifica. Quando un thread deve modificare l'ID pagina della pagina principale, deve acquisire il latch in modalità EX.
Perché la pagina principale di un b-tree dovrebbe mai cambiare? Man mano che il numero di record di indice nella pagina principale aumenta, alla fine si riempirà e si verificherà una divisione della pagina. Quando ciò accade, la pagina radice corrente e la pagina in cui si divide diventano un nuovo livello nell'albero b e viene creata una pagina radice nuova di zecca, con due record di indice, che puntano alla vecchia pagina radice e alla pagina in cui si trova diviso in. Il nuovo ID della pagina principale deve essere inserito nei metadati, quindi il latch viene acquisito in modalità EX. Ciò accadrà alcune volte rapidamente quando un indice su una tabella vuota inizia a essere popolato da inserti, ma non è qualcosa che vedrai come un problema di collo di bottiglia delle prestazioni in corso.
Riepilogo
Come sicuramente avrai appreso da questa serie, la comprensione dei latch e dei colli di bottiglia dei latch implica conoscere un po' di più su ciò che sta accadendo all'interno del motore di archiviazione rispetto all'analisi generale delle statistiche di attesa.
Di solito consiglio alle persone di *non* di iniziare la risoluzione dei problemi relativi alle prestazioni guardando le statistiche sui latch (tramite sys.dm_os_latch_stats ) ma invece di iniziare sempre con le statistiche di attesa (vedi il mio post qui) e approfondire i latch solo se LATCH_EX o LATCH_SH sono una delle prime poche attese sull'istanza di SQL Server.
Se hai domande sui fermi, non esitare a scrivermi.