Database
 sql >> Database >  >> RDS >> Database

Tagliare il grasso del registro delle transazioni

In molti carichi di lavoro di SQL Server, in particolare OLTP, il registro delle transazioni del database può rappresentare un collo di bottiglia che aumenta il tempo necessario per il completamento di una transazione. La maggior parte delle persone presume che il sottosistema di I/O sia il vero collo di bottiglia, poiché non è in grado di tenere il passo con la quantità di log delle transazioni generata dal carico di lavoro.

Latenza di scrittura del registro delle transazioni

La latenza delle operazioni di scrittura nel registro delle transazioni può essere monitorata utilizzando il sys.dm_io_virtual_file_stats DMV e correlato al WRITELOG attese che si verificano sul sistema. Ho registrato un video dimostrativo sull'analisi dell'I/O del registro delle transazioni nel 2011, quindi non ripeterò tutto in questo post. Puoi ottenere il video qui e il codice demo qui (adatto per essere eseguito subito in produzione).

Se la latenza di scrittura è superiore a quella che ti aspetteresti per il tuo sottosistema di I/O, il sottosistema di I/O non può tenere il passo, come è supposizione generale. Ciò significa che il sottosistema di I/O deve essere migliorato? Non necessariamente.

Su molti sistemi client ho riscontrato che una parte significativa dei record di registro generati non è necessaria e se è possibile ridurre il numero di record di registro generati, si riduce la quantità di registro delle transazioni che viene scritto su disco. Ciò dovrebbe tradursi in una riduzione della latenza di scrittura, riducendo così i tempi di completamento delle transazioni.

Esistono due cause principali della generazione di record di log estranei:indici non cluster non utilizzati e indici che diventano frammentati.

Indici non cluster non utilizzati

Ogni volta che un record viene inserito in una tabella, è necessario inserire un record in ogni indice non cluster definito nella tabella (ad eccezione degli indici filtrati con filtri appropriati, che da questo momento ignorerò). Ciò significa che vengono generati record di registro aggiuntivi, almeno uno per indice non cluster, per ogni inserto di tabella. La stessa cosa si applica all'eliminazione di un record in una tabella:i record corrispondenti devono essere eliminati da tutti gli indici non cluster. Per un aggiornamento di un record di tabella, i record dell'indice non cluster vengono aggiornati solo se le colonne della chiave dell'indice non cluster o le colonne incluse facevano parte dell'aggiornamento.

Queste operazioni sono necessarie, ovviamente, per mantenere ogni indice non cluster corretto rispetto alla tabella, ma se l'indice non cluster non è utilizzato dal carico di lavoro, le operazioni ei record di log da esse prodotti non sono un sovraccarico. Inoltre, se questi indici inutilizzati si frammentano (cosa di cui parlerò più avanti in questo post), anche le normali attività di manutenzione dell'indice funzioneranno su di essi, generando ancora più record di log (dall'indice REBUILD o REORGANIZE operazioni) del tutto inutilmente.

Gli indici inutilizzati provengono da diverse origini, ad esempio qualcuno che crea erroneamente un indice per colonna di tabella, qualcuno che crea tutti gli indici suggeriti dai DMV degli indici mancanti o qualcuno che crea tutti gli indici suggeriti da Ottimizzazione guidata database. Potrebbe anche essere che le caratteristiche del carico di lavoro siano cambiate e quindi quelli che erano utili indici non vengono più utilizzati.

Ovunque provengano, gli indici inutilizzati dovrebbero essere rimossi per ridurre il loro sovraccarico. Puoi determinare quali indici non sono utilizzati utilizzando il DMV sys.dm_db_index_usage_stats e ti consiglio di leggere i post dei miei colleghi Kimberly L. Tripp (qui) e Joe Sack (qui e qui), poiché spiegano come utilizzare correttamente il DMV.

Frammentazione dell'indice

La maggior parte delle persone pensa alla frammentazione dell'indice come a un problema che interessa le query che devono leggere grandi quantità di dati. Sebbene questo sia uno dei problemi che la frammentazione può causare, la frammentazione è anche un problema a causa del modo in cui si verifica.

La frammentazione è causata da un'operazione chiamata divisione di pagina. La causa più semplice di una divisione di pagina è quando un record di indice deve essere inserito in una pagina particolare (a causa del suo valore chiave) e la pagina non ha spazio libero sufficiente. In questo scenario, avranno luogo le seguenti operazioni:

  • Viene allocata e formattata una nuova pagina di indice
  • Alcuni dei record della pagina intera vengono spostati nella nuova pagina, creando così spazio libero nella pagina richiesta
  • La nuova pagina è collegata alla struttura dell'indice
  • Il nuovo record viene inserito nella pagina richiesta

Tutte queste operazioni generano record di registro e, come puoi immaginare, questo può essere molto più di quanto necessario per inserire un nuovo record in una pagina che non richiede una divisione di pagina. Nel 2009 ho pubblicato sul blog un'analisi del costo della divisione della pagina in termini di registro delle transazioni e ho trovato alcuni casi in cui una divisione della pagina generava oltre 40 volte più registro delle transazioni rispetto a un normale inserto!

Il primo passo per ridurre il costo aggiuntivo è rimuovere gli indici inutilizzati, come ho descritto sopra, in modo che non generino divisioni di pagina. Il secondo passaggio consiste nell'identificare gli indici rimanenti che stanno diventando frammentati (e quindi devono subire divisioni di pagina) utilizzando sys.dm_db_index_physical_stats DMV (o il nuovo SQL Sentry Fragmentation Manager) e creando in modo proattivo spazio libero al loro interno utilizzando un fattore di riempimento dell'indice. Un fillfactor indica a SQL Server di lasciare uno spazio vuoto nelle pagine dell'indice quando l'indice viene compilato, ricostruito o riorganizzato in modo che vi sia spazio per consentire l'inserimento di nuovi record senza richiedere una divisione della pagina, riducendo così i record di registro aggiuntivi generati.

Ovviamente nulla è gratuito:il compromesso quando si utilizzano i fillfactor è che si fornisce in modo proattivo spazio aggiuntivo negli indici per impedire la generazione di più record di registro, ma di solito è un buon compromesso da fare. La scelta di un fillfactor è relativamente facile e ne ho parlato nel blog qui.

Riepilogo

Ridurre la latenza di scrittura di un file di registro delle transazioni non significa sempre passare a un sottosistema di I/O più veloce o separare il file nella propria parte del sottosistema di I/O. Con una semplice analisi degli indici nel database, potresti essere in grado di ridurre significativamente la quantità di record di log delle transazioni generati, portando a una riduzione proporzionata della latenza di scrittura.

Esistono altri problemi più sottili che possono influire sulle prestazioni del registro delle transazioni e li esplorerò in un post futuro.