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

Memorizzazione nella cache di oggetti temporanei di SQL Server

La creazione di una tabella è un'operazione che richiede tempo e risorse relativamente elevate. Il server deve individuare e allocare spazio di archiviazione per i nuovi dati e le strutture degli indici e creare le voci corrispondenti in più tabelle di metadati di sistema. Tutto questo lavoro deve essere svolto in modi che funzionino sempre correttamente in condizioni di concorrenza elevata e che soddisfino tutte le garanzie ACID previste per un database relazionale.

In SQL Server, ciò significa adottare i tipi corretti di blocchi e latch, nella sequenza corretta, assicurando anche che le voci dettagliate del registro delle transazioni vengano salvate in modo sicuro nell'archiviazione permanente prima di qualsiasi modifica fisica al database. Queste voci di registro assicurano che il sistema possa riportare il database a uno stato coerente in caso di ripristino di una transazione o arresto anomalo del sistema.

L'eliminazione di una tabella è un'operazione altrettanto costosa. Fortunatamente, la maggior parte dei database non crea o elimina tabelle con grande frequenza. L'ovvia eccezione a questo è il database di sistema tempdb . Questo singolo database contiene l'archiviazione fisica, le strutture di allocazione, i metadati di sistema e le voci del registro delle transazioni per tutte le tabelle temporanee e le variabili di tabella nell'intera istanza di SQL Server.

È nella natura delle tabelle temporanee e delle variabili di tabella essere create ed eliminate molto più frequentemente rispetto ad altri tipi di oggetti di database. Quando questa frequenza naturalmente elevata di creazione e distruzione è combinata con l'effetto di concentrazione di tutte le tabelle temporanee e le variabili di tabella associate a un singolo database, non sorprende che possano sorgere contese nelle strutture di allocazione e metadati del tempdb banca dati.

Memorizzazione temporanea di oggetti nella cache

Per ridurre l'impatto su tempdb strutture, SQL Server può memorizzare nella cache oggetti temporanei per il riutilizzo. Anziché eliminare un oggetto temporaneo, SQL Server conserva i metadati di sistema e tronca i dati della tabella. Se la tabella è di 8 MB o inferiore, il troncamento viene eseguito in modo sincrono; altrimenti viene utilizzata la caduta differita. In entrambi i casi, il troncamento riduce i requisiti di archiviazione a una singola pagina di dati (vuota) e le informazioni di allocazione a una singola pagina IAM.

La memorizzazione nella cache evita quasi tutti i costi di allocazione e metadati della creazione dell'oggetto temporaneo la prossima volta. Come effetto collaterale di apportare meno modifiche al tempdb database rispetto a un ciclo completo di eliminazione e ricreazione, la memorizzazione temporanea degli oggetti nella cache riduce anche la quantità di registrazione delle transazioni richiesta.

Raggiungere la memorizzazione nella cache

Le variabili di tabella e le tabelle temporanee locali possono essere entrambe memorizzate nella cache. Per qualificarsi per la memorizzazione nella cache, una tabella temporanea locale o una variabile di tabella deve essere creato in un modulo:

  • Stored procedure (inclusa una stored procedure temporanea)
  • Innesco
  • Funzione con valori di tabella a più istruzioni
  • Funzione scalare definita dall'utente

Il valore di ritorno di una funzione con valori di tabella a più istruzioni è una variabile di tabella, che può essere essa stessa memorizzata nella cache. I parametri con valori di tabella (che sono anche variabili di tabella) possono essere memorizzati nella cache quando il parametro viene inviato da un'applicazione client, ad esempio nel codice .NET utilizzando SqlDbType.Structured . Quando l'istruzione è parametrizzata, le strutture dei parametri con valori di tabella possono essere memorizzate nella cache solo in SQL Server 2012 o versioni successive.

I seguenti non possono essere memorizzato nella cache:

  • Tabelle temporanee globali
  • Oggetti creati utilizzando SQL ad hoc
  • Oggetti creati utilizzando SQL dinamico (ad es. utilizzando EXECUTE o sys.sp_executesql )

Per essere memorizzato nella cache, un oggetto temporaneo inoltre non deve :

  • Avere vincoli con nome (i vincoli senza nomi espliciti vanno benissimo)
  • Esegui "DDL" dopo la creazione dell'oggetto
  • Essere in un modulo definito usando il WITH RECOMPILE opzione
  • Fai chiamare usando il WITH RECOMPILE opzione del EXECUTE dichiarazione

Per affrontare esplicitamente alcune idee sbagliate comuni:

  • TRUNCATE TABLE non impedire la memorizzazione nella cache
  • DROP TABLE non impedire la memorizzazione nella cache
  • UPDATE STATISTICS non impedire la memorizzazione nella cache
  • La creazione automatica di statistiche non impedire la memorizzazione nella cache
  • Manuale CREATE STATISTICS farà impedire la memorizzazione nella cache

Tutti gli oggetti temporanei in un modulo vengono valutati separatamente per l'idoneità alla memorizzazione nella cache. Un modulo che contiene uno o più oggetti temporanei che non possono essere memorizzati nella cache può comunque qualificarsi per la memorizzazione nella cache di altri oggetti temporanei all'interno dello stesso modulo.

Un modello comune che disabilita la memorizzazione nella cache per le tabelle temporanee è la creazione di indici dopo l'istruzione di creazione della tabella iniziale. Nella maggior parte dei casi è possibile aggirare questo problema utilizzando la chiave primaria e vincoli univoci. In SQL Server 2014 e versioni successive, abbiamo la possibilità di aggiungere indici non univoci non cluster direttamente nell'istruzione di creazione della tabella utilizzando INDEX clausola.

Monitoraggio e manutenzione

Possiamo vedere quanti oggetti temporanei sono attualmente memorizzati nella cache utilizzando i contatori di cache DMV:

SELECT
    DOMCC.[type],
    DOMCC.pages_kb,
    DOMCC.pages_in_use_kb,
    DOMCC.entries_count,
    DOMCC.entries_in_use_count
FROM sys.dm_os_memory_cache_counters AS DOMCC 
WHERE 
    DOMCC.[name] = N'Temporary Tables & Table Variables';

Un esempio di risultato è:

Una voce della cache è considerata in uso fintanto che qualsiasi parte del modulo contenitore è in esecuzione. Le esecuzioni simultanee dello stesso modulo comporteranno la creazione di più oggetti temporanei memorizzati nella cache. Più piani di esecuzione per lo stesso modulo (forse a causa di sessioni differenti SET opzioni) porterà anche a più voci di cache per lo stesso modulo.

Le voci della cache possono essere invecchiate nel tempo in risposta a esigenze contrastanti di memoria. Gli oggetti temporanei memorizzati nella cache possono anche essere rimossi (in modo asincrono, tramite un thread di sistema in background) quando il piano di esecuzione del modulo padre viene rimosso dalla cache del piano.

Sebbene non sia supportato (o in alcun modo consigliato) per i sistemi di produzione, l'archivio cache degli oggetti temporanei può essere completamente cancellato manualmente a scopo di test con:

DBCC FREESYSTEMCACHE('Temporary Tables & Table Variables')
    WITH MARK_IN_USE_FOR_REMOVAL;
WAITFOR DELAY '00:00:05';

Il ritardo di cinque secondi consente l'esecuzione dell'attività di pulizia in background. Tieni presente che questo comando è davvero pericoloso . Dovresti utilizzarlo (a tuo rischio) solo su un'istanza di test a cui hai accesso esclusivo. Al termine del test, riavvia l'istanza di SQL Server.

Dettagli sull'implementazione della memorizzazione nella cache

Le variabili di tabella sono implementate da una tabella utente "reale" nel tempdb database (sebbene non sia una tabella che possiamo interrogare direttamente). Il nome della tabella associata è "#" seguito dalla rappresentazione esadecimale a otto cifre dell'ID oggetto. La query seguente mostra la relazione:

-- A table variable
DECLARE @Z AS table (z integer NULL);
 
-- Corresponding sys.tables entry
SELECT
    T.[name],
    ObjIDFromName = CONVERT(integer, CONVERT(binary(4), RIGHT(T.[name], 8), 2)),
    T.[object_id],
    T.[type_desc],
    T.create_date,
    T.modify_date
FROM tempdb.sys.tables AS T 
WHERE
    T.[name] LIKE N'#[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]';

Di seguito viene mostrato un risultato di esempio. Nota come l'ID oggetto calcolato dal nome dell'oggetto corrisponda all'ID oggetto effettivo:

L'esecuzione di quello script come SQL ad hoc produrrà un tempdb diverso ID oggetto (e nome oggetto) su ogni esecuzione (nessuna memorizzazione nella cache). L'inserimento dello stesso script all'interno di un modulo (ad es. una procedura memorizzata) consentirà di memorizzare nella cache la variabile della tabella (purché l'SQL dinamico non venga utilizzato), in modo che l'ID e il nome dell'oggetto siano gli stessi a ogni esecuzione.

Quando la variabile di tabella non è memorizzata nella cache, la tabella sottostante viene creata ed eliminata ogni volta. Quando è abilitata la memorizzazione temporanea degli oggetti nella cache, la tabella viene troncata alla fine del modulo invece di essere eliminata. nessuna modifica ai metadati di sistema quando una variabile di tabella viene memorizzata nella cache. L'impatto sulle strutture di allocazione e sulla registrazione delle transazioni è limitato all'eliminazione delle righe nella tabella e alla rimozione di eventuali dati e pagine di allocazione in eccesso al termine del modulo.

Tabelle temporanee

Quando viene utilizzata una tabella temporanea al posto di una variabile di tabella, il meccanismo di base è essenzialmente lo stesso, con solo un paio di passaggi aggiuntivi per la ridenominazione:Quando una tabella temporanea non è memorizzata nella cache , è visibile in tempdb con il familiare nome fornito dall'utente, seguito da una serie di trattini bassi e dalla rappresentazione esadecimale dell'ID oggetto come suffisso finale. La tabella temporanea locale rimane finché non viene eliminata in modo esplicito o fino al termine dell'ambito in cui è stata creata. Per SQL ad hoc, questo significa quando la sessione si disconnette dal server.

Per una tabella temporanea memorizzata nella cache , la prima volta che il modulo viene eseguito, la tabella temporanea viene creata proprio come per il caso non memorizzato nella cache. Alla fine del modulo, invece di essere eliminata automaticamente (in quanto finisce l'ambito in cui è stata creata), la tabella temporanea viene troncata e quindi rinominata alla rappresentazione esadecimale dell'ID oggetto (esattamente come visto per la variabile tabella). Alla successiva esecuzione del modulo, la tabella memorizzata nella cache viene rinominata dal formato esadecimale al nome fornito dall'utente (più caratteri di sottolineatura più ID oggetto esadecimale).

Le operazioni di ridenominazione extra all'inizio e alla fine del modulo implicano un numero limitato di modifiche ai metadati del sistema . Pertanto, le tabelle temporanee memorizzate nella cache possono ancora riscontrare almeno alcuni conflitti di metadati con tassi di riutilizzo molto elevati. Tuttavia, l'impatto sui metadati di una tabella temporanea memorizzata nella cache è molto inferiore rispetto al caso non memorizzato nella cache (creazione ed eliminazione della tabella ogni volta).

Maggiori dettagli ed esempi di come funziona la memorizzazione nella cache di oggetti temporanei possono essere trovati nel mio articolo precedente.

Statistiche sulle tabelle temporanee memorizzate nella cache

Come accennato in precedenza, le statistiche possono essere automaticamente creato su tabelle temporanee senza perdere i vantaggi della memorizzazione temporanea degli oggetti nella cache (ricorda che la creazione manuale delle statistiche si disabilitare la memorizzazione nella cache).

Un avvertimento importante è che le statistiche associati a una tabella temporanea memorizzata nella cache non vengono reimpostati quando l'oggetto viene memorizzato nella cache alla fine del modulo o quando l'oggetto memorizzato nella cache viene recuperato dalla cache all'inizio del modulo. Di conseguenza, le statistiche su una tabella temporanea memorizzata nella cache possono essere lasciate da un'esecuzione precedente non correlata. In altre parole, le statistiche possono avere assolutamente nessuna relazione al contenuto corrente della tabella temporanea.

Questo è ovviamente indesiderabile, dato che il motivo principale per preferire una tabella temporanea locale rispetto a una variabile di tabella è la disponibilità di statistiche di distribuzione accurate. In mitigazione, le statistiche verranno aggiornate automaticamente quando (se) il numero accumulato di modifiche all'oggetto memorizzato nella cache sottostante raggiunge la soglia di ricompilazione interna. Questo è difficile da valutare in anticipo, perché i dettagli sono complessi e alquanto controintuitivi.

La soluzione più completa, pur mantenendo i vantaggi della memorizzazione temporanea degli oggetti nella cache, consiste nel:

  • Manualmente UPDATE STATISTICS sulla tabella temporanea all'interno del modulo; e
  • Aggiungi un OPTION (RECOMPILE) suggerimento alle affermazioni che fanno riferimento alla tabella temporanea

Naturalmente ciò comporta un costo, ma il più delle volte è accettabile. Infatti, scegliendo in primo luogo di utilizzare una tabella temporanea locale, l'autore del modulo afferma implicitamente che è probabile che la selezione del piano sia sensibile al contenuto della tabella temporanea, quindi la ricompilazione può avere senso. L'aggiornamento manuale delle statistiche garantisce che le statistiche utilizzate durante la ricompilazione riflettano il contenuto corrente della tabella (come sicuramente ci aspetteremmo).

Per maggiori dettagli su come funziona esattamente, vedere il mio precedente articolo sull'argomento.

Riepilogo e raccomandazioni

La memorizzazione temporanea degli oggetti all'interno di un modulo può ridurre notevolmente la pressione sull'allocazione condivisa e sulle strutture dei metadati nel tempdb Banca dati. La riduzione maggiore si verificherà quando si utilizzano variabili di tabella perché la memorizzazione nella cache e il riutilizzo di questi oggetti temporanei non comportano la modifica dei metadati (nessuna operazione di ridenominazione). La contesa sulle strutture di allocazione può ancora essere vista se la singola pagina di dati memorizzata nella cache non è sufficiente per contenere tutti i dati della variabile di tabella in fase di esecuzione.

L'impatto sulla qualità del piano dovuto alla mancanza di informazioni sulla cardinalità per le variabili di tabella può essere mitigato utilizzando OPTION(RECOMPILE) o flag di traccia 2453 (disponibile da SQL Server 2012 in poi). Tieni presente che queste attenuazioni forniscono all'ottimizzatore solo informazioni sul numero totale di righe nella tabella.

Per generalizzare, variabili di tabella vengono utilizzati al meglio quando i dati sono piccoli (adattandosi idealmente all'interno di una singola pagina di dati per ottenere i massimi vantaggi dalla contesa) e quando la selezione del piano non dipende dai valori presenti nella variabile della tabella.

Se informazioni sulla distribuzione dei dati (densità e istogrammi) è importante per la selezione del piano, utilizzare una tabella temporanea locale invece. Assicurati di soddisfare le condizioni per la memorizzazione temporanea della tabella nella cache, che molto spesso significa non creare indici o statistiche dopo l'istruzione di creazione della tabella iniziale. Ciò è reso più conveniente da SQL Server 2014 in poi grazie all'introduzione di INDEX clausola del CREATE TABLE dichiarazione.

Un esplicito UPDATE STATISTICS dopo che i dati sono stati caricati nella tabella temporanea e OPTION (RECOMPILE) suggerimenti sulle istruzioni che fanno riferimento alla tabella potrebbero essere necessari per produrre tutti i vantaggi previsti delle tabelle temporanee memorizzate nella cache all'interno di un modulo.

È importante utilizzare oggetti temporanei solo quando producono un chiaro vantaggio, il più delle volte in termini di qualità del piano. L'uso eccessivo, inefficiente o non necessario di oggetti temporanei può portare a tempdb contesa, anche quando viene raggiunta la memorizzazione temporanea degli oggetti nella cache.

La memorizzazione temporanea ottimale degli oggetti nella cache potrebbe non essere sufficiente per ridurre tempdb contesa a livelli accettabili in tutti i casi, anche quando gli oggetti temporanei vengono utilizzati solo quando pienamente giustificati. L'uso di variabili di tabella in memoria o tabelle in memoria non durevoli può fornire soluzioni mirate in questi casi, anche se ci sono sempre dei compromessi da fare e nessuna singola soluzione rappresenta attualmente l'opzione migliore in tutti i casi.