Trova le dimensioni non compresse di tutte le tabelle in un database


12

In Dynamics AX esiste un meccanismo di memorizzazione nella cache in cui le tabelle possono essere configurate per essere caricate in memoria e memorizzate nella cache. Questa cache è limitata a una determinata quantità di KB per evitare problemi di memoria. L'impostazione di cui sto parlando viene chiamata entiretablecachee carica l'intera tabella in memoria non appena viene richiesto un singolo record.

Fino a poco tempo fa abbiamo fatto affidamento su alcuni script per verificare la dimensione delle tabelle con questa impostazione per vedere se la dimensione della tabella è superiore a questo limite.

Ora, tuttavia, entra in gioco la compressione e cose come sp_spaceused o sys.allocation_units sembrano riportare lo spazio effettivamente utilizzato dai dati compressi.

Ovviamente, il server delle applicazioni sta lavorando con dati non compressi, quindi la dimensione dei dati sul disco in SQL Server è irrilevante. Ho bisogno delle dimensioni effettive che avranno i dati non compressi.

Conosco sp_estimate_data_compression_savings ma come dice il nome, questa è solo una stima.
Preferirei avere le dimensioni più corrette possibili.

L'unico modo in cui ho potuto pensare è stato un SQL dinamico contorto che creava tabelle non compresse con la stessa struttura delle tabelle compresse, inserendo i dati compressi in quella tabella shadow e quindi controllando le dimensioni di quella tabella shadow.
Inutile dire che questo è un po 'noioso e richiede un po' di tempo per essere eseguito su un database di diverse centinaia di GB.

Powershell potrebbe essere un'opzione, ma non vorrei scorrere su tutte le tabelle per eseguirne una select *per controllare le dimensioni dello script in quanto ciò inonderebbe semplicemente la cache e probabilmente richiederebbe anche molto tempo.

In breve, ho bisogno di un modo per ottenere la dimensione di ogni tabella poiché sarà una volta non compressa e con frammentazione fuori dall'equazione presentata all'applicazione, se possibile. Sono aperto a diversi approcci, T-SQL è preferito ma non sono contrario a Powershell o altri approcci creativi.

Supponiamo che il buffer nell'applicazione sia la dimensione dei dati. Un bigint ha sempre le dimensioni di un bigint e un tipo di dati carattere è di 2 byte per carattere (unicode). Anche i dati BLOB prendono le dimensioni dei dati, un enum è sostanzialmente un int e i dati numerici sono numerici (38,12), datetime è la dimensione di un datetime. Inoltre, non ci sono NULLvalori, sono memorizzati come una stringa vuota 1900-01-01o zero.

Non c'è documentazione su come questo sia implementato, ma i presupposti si basano su alcuni test e sugli script utilizzati da PFE e dal team di supporto (che a quanto pare ignorano anche la compressione, poiché il controllo è incorporato nell'applicazione e l'app non può dire se i dati sottostanti sono compressi) che controllano anche le dimensioni della tabella. Questo link ad esempio afferma:

Evita di utilizzare le cache di Tutta la tabella per tabelle di grandi dimensioni (in AX 2009 su 128 KB o 16 pagine, in AX 2012 sull'impostazione dell'applicazione "Dimensione cache intera tabella" [impostazione predefinita: 32 KB o 4 pagine]) - passa invece alla memorizzazione nella cache dei record.


3
È confuso, ma forse una copia ripristinata con la compressione disabilitata sarebbe la più precisa. Quindi stai anche testando i ripristini, il che ti fa sembrare un DBA TOP 1.
Erik Darling,

Credi che sarebbe la tua scommessa migliore. Potrebbero esserci dei modi per provare a fare la matematica. Quante righe si moltiplicano per tipi di dati e lunghezze di colonne definiti, quindi si aggiungono negli indici, ecc. È molto più lavoro che scrivere script sul ripristino e disabilitare la compressione @sp_BlitzErik suggerisce sopra. E chi non vorrebbe essere un DBA TOP 1?
Mike Walsh,

SUM (datalength ()) per tutte le colonne ottenere dimensioni dei dati non compresse?
Tapakah Ua,

@sp_BlitzErik Potrebbe essere una risposta anziché un commento.
Tom V - prova topanswers.xyz il

Risposte:


7

Ho bisogno delle dimensioni effettive che avranno i dati non compressi.
...
Preferirei avere le dimensioni più corrette possibili.

Mentre il desiderio di queste informazioni è certamente comprensibile, ottenere queste informazioni, specialmente nel contesto del "corretto possibile", è più complicato di quanto tutti si aspettino a causa di ipotesi errate. Sia che si tratti dell'idea della tabella shadow non compressa menzionata nella domanda, o del suggerimento di @ sp_BlitzErik in un commento sul ripristino del DB e della decompressione lì per verificare, non si deve presumere che la dimensione della tabella non compressa == la dimensione di detti dati in memoria sul server app:

  1. Sono tutte le righe della tabella di essere memorizzati nella cache? O semplicemente entro un raggio? Il presupposto qui è che è tutto, e questo potrebbe essere corretto, ma ho pensato che si dovrebbe almeno menzionare che questo potrebbe non essere il caso (a meno che la documentazione non indichi diversamente, ma questo è comunque un punto minore, semplicemente non volevo da non menzionare).

    La domanda è stata aggiornata per affermare: sì, tutte le righe vengono memorizzate nella cache.

  2. Struttura ambientale

    1. Sul lato DB:
      Pagina e sovraccarico di riga sul lato DB: quante righe si adattano a una pagina è determinata da molti fattori che potrebbero eliminare le stime. Anche con un valore FILLFACTORdi 100 (o 0), è probabile che rimanga spazio inutilizzato sulla pagina a causa del fatto che non è sufficiente per un'intera riga. E questo è in aggiunta all'intestazione della pagina. Inoltre, se è abilitata una funzionalità di Isolamento snapshot, ci saranno, credo, altri 13 byte per riga occupati dal numero di versione, e questo eliminerà le stime. Esistono altri minutia relativi alle dimensioni effettive della riga (bitmap NULL, colonne a lunghezza variabile, ecc.), Ma gli elementi menzionati finora dovrebbero essere il punto.
    2. Sul lato server app:
      quale tipo di raccolta viene utilizzata per archiviare i risultati memorizzati nella cache? Presumo che questa sia un'app .NET, quindi è una DataTable? Un elenco generico? Un SortedDictionary? Ogni tipo di raccolta ha una diversa quantità di udito. Non mi aspetterei che nessuna delle opzioni rispecchi necessariamente le spese generali di Page e Row sul lato DB, specialmente su scala (sono sicuro che una piccola quantità di righe potrebbe non avere abbastanza varie da importare, ma non stai cercando differenze in centinaia di byte o solo pochi kB).
  3. Tipi di dati
    1. Sul lato DB:
      CHAR/ i VARCHARdati sono memorizzati a 1 byte per carattere (ignorando i caratteri a doppio byte per il momento). XMLè ottimizzato per non occupare quasi lo spazio necessario per la rappresentazione del testo. Questo tipo di dati crea un dizionario di nomi di elementi e attributi e sostituisce i riferimenti reali ad essi nel documento con i loro rispettivi ID (un po 'carino, in realtà). Altrimenti, i valori di stringa sono tutti UTF-16 (2 o 4 byte per "carattere"), proprio come NCHAR/ NVARCHAR. DATETIME2è compreso tra 6 e 8 byte. DECIMALè compreso tra 5 e 17 byte (a seconda della precisione).
    2. Sul lato server delle app: le
      stringhe (di nuovo, supponendo che .NET) siano sempre UTF-16. Non esiste ottimizzazione per stringhe a 8 bit come quelle contenute VARCHAR. MA, le stringhe possono anche essere "internate", che è una copia condivisa a cui è possibile fare riferimento più volte (ma non so se funziona per le stringhe nelle raccolte o, in tal caso, se funziona per tutti i tipi di raccolte). XMLpuò o non può essere memorizzato allo stesso modo in memoria (dovrò cercarlo). DateTimeè sempre 8 byte (come T-SQL DATETIME, ma non come DATE, TIMEo DATETIME2). Decimalè sempre 16 byte .

Tutto questo per dire: non c'è praticamente nulla che tu possa fare sul lato DB per ottenere dimensioni di footprint di memoria anche abbastanza accurate sul lato server delle app. È necessario trovare un modo per interrogare lo stesso server delle app, dopo essere stato caricato con una tabella particolare, quindi sapere quanto è grande. E non sono sicuro se un debugger ti consentirebbe di visualizzare le dimensioni di runtime di una raccolta riempita. In caso contrario, l'unico modo per avvicinarsi sarebbe quello di passare attraverso tutte le righe di una tabella, moltiplicando ogni colonna per la dimensione .NET appropriata (ad esempio INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃, ecc.), Ma ciò lascia ancora la domanda dell'overhead della collezione più ogni elemento della collezione.

Data una nuova definizione nella domanda, si potrebbe probabilmente fare la seguente query per avvicinarsi piuttosto. E non importa se la tabella è compressa o meno, anche se spetta a ogni persona determinare se la scansione di tutte le righe è appropriata per la produzione (magari da un ripristino o durante le ore non di punta):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Ma ricorda, questo non tiene conto del sovraccarico di elementi di raccolta o raccolta. E non sono sicuro se possiamo ottenere quel valore senza un debugger (o forse qualcosa come ILSpy, ma non lo sto raccomandando perché potrebbe violare l'EULA a seconda delle leggi locali).


Abbiamo finito per implementare i controlli nel codice per essere sicuri della dimensione del buffer quando viene presentato all'applicazione.
Tom V - prova topanswers.xyz il

6

Dalla tua domanda sembra che tu abbia una dimensione massima della cache Se non desideri caricare tabelle nella cache che superano tale dimensione. Se questo è vero, non è necessario conoscere la dimensione esatta di ogni tabella. Devi solo sapere se una tabella è più grande o più piccola della dimensione massima della cache S. Questo è un problema significativamente più semplice a seconda delle definizioni delle colonne delle tabelle e del conteggio delle righe.

Concordo con la grande risposta di Solomon Rutzky in quanto guardare dati non compressi non è la strada da percorrere e potrebbe essere difficile trovare una buona approssimazione per la dimensione reale di una tabella nella cache. Tuttavia, lavorerò nel quadro della domanda e presumo che sia possibile sviluppare una formula abbastanza vicina in base alle definizioni delle colonne per i tipi di dati statici e la lunghezza effettiva delle colonne dinamiche.

Se hai quella mappatura dei tipi di dati per la dimensione della cache, dovresti essere in grado di valutare alcune tabelle senza nemmeno guardare i dati in esse:

  1. Se una tabella ha solo tipi di dati statici (senza stringhe o BLOB), è possibile approssimare il numero di righe osservando sys.partitionse calcolando la dimensione della tabella utilizzando le definizioni di colonna.
  2. Se una tabella con molte righe ha colonne di tipo di dati statici sufficienti, potresti essere in grado di eliminarlo come troppo grande senza guardare i suoi dati. Ad esempio, una tabella con 10 milioni di righe e 5 BIGINTcolonne potrebbe avere la dimensione di quei dati dimensionati come 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M byte che potrebbero essere più grandi del limite della dimensione della cache S. Non importa se ha anche un sacco di colonne stringa.
  3. Se una tabella con poche righe è abbastanza piccola, potresti essere in grado di confermare che è inferiore al limite semplicemente assumendo che ogni tipo di dati dinamico abbia la dimensione massima possibile. Ad esempio, una tabella di 100 righe con una BIGINTcolonna e una NVARCHAR(20)colonna non può superare 100 * (8 + 2 * 20) = 4800 byte.
  4. Potrebbe essere vero che se una tabella ha una dimensione compressa in SQL Server che è più grande di alcuni fattori S, è estremamente improbabile che si adatti alla cache. Dovresti fare dei test per capire se esiste un valore del genere.
  5. Potresti essere fortunato nel fatto che tutte le colonne dinamiche hanno statistiche su di esse. Le statistiche contengono informazioni sulla lunghezza media e che possono essere sufficientemente accurate per i tuoi scopi.

Potrebbe essere necessario eseguire una query sui dati delle tabelle che non soddisfano nessuno dei criteri sopra indicati. Esistono alcuni trucchi che è possibile utilizzare per ridurre al minimo l'impatto sulle prestazioni di questo. Direi che qui hai due priorità in competizione: apprezzi la precisione ma non vuoi scansionare tutti i dati nel tuo database. Potrebbe essere possibile aggiungere una sorta di buffer ai tuoi calcoli. Non so se sia più accettabile escludere una tabella leggermente al di sotto della dimensione massima della cache So includere una tabella leggermente al di sopra della dimensione massima della cache.

Ecco alcune idee per rendere più veloci le query che esaminano i dati della tabella:

  1. Per le tabelle di grandi dimensioni potresti essere in grado di utilizzare TABLESAMPLEpurché le dimensioni del campione siano sufficientemente grandi.
  2. Per le tabelle di grandi dimensioni con una chiave cluster può essere utile elaborarle in batch sulla chiave cluster. Sfortunatamente non conosco un modo per calcolare un SUM()che si chiude presto in base al valore di tale aggregato. Ho visto solo quel lavoro per ROW_NUMBER(). Ma potresti scansionare il primo 10% della tabella, salvare la dimensione dei dati calcolati, scansionare il 10% successivo e così via. Per le tabelle troppo grandi per la cache, è possibile salvare una notevole quantità di lavoro con questo approccio chiudendo in anticipo.
  3. Per alcune tabelle potresti essere abbastanza fortunato da avere indici di copertura su tutte le colonne dinamiche. A seconda delle dimensioni della riga o di altri fattori, la scansione di ciascun indice alla volta potrebbe essere più veloce di una scansione della tabella. Potresti anche uscire presto da questo processo se la dimensione della tabella è troppo grande dopo aver letto un indice su una singola colonna.
  4. Le lunghezze medie delle colonne dinamiche potrebbero non cambiare molto nel tempo. Potrebbe essere pratico salvare le lunghezze medie calcolate e utilizzare quei valori nei calcoli per un po '. È possibile ripristinare questi valori in base all'attività DML nelle tabelle o in base ad altre metriche.
  5. Se è possibile eseguire test su tutte le tabelle per sviluppare un algoritmo, è possibile sfruttare i modelli nei dati. Ad esempio, se si elaborano tabelle che iniziano con il più piccolo per primo, è possibile che una volta elaborate 10 (ho composto questo numero) tabelle in una riga troppo grandi per la cache, è molto improbabile che qualsiasi tabella più grande si adatti alla cache. Questo potrebbe essere accettabile se va bene escludere alcune tabelle che potrebbero essere contenute nella cache.

Mi rendo conto di non aver incluso alcun codice SQL in questa risposta. Fammi sapere se sarebbe utile scrivere il codice demo per una qualsiasi delle idee che ho discusso qui.


2
Non avevo pensato all'approccio di escludere tabelle del genere, mi piace l'approccio
Tom V - prova topanswers.xyz il
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.