LOB_DATA, scansioni lente della tabella e alcune domande di I / O


19

Ho una tabella piuttosto grande con una delle colonne che è un dato XML con una dimensione media della voce XML di ~ 15 kilobyte. Tutte le altre colonne sono ints regolari, origini, GUID, ecc. Per avere alcuni numeri concreti, supponiamo che la tabella abbia un milione di righe e abbia una dimensione di ~ 15 GB.

Quello che ho notato è che questa tabella è davvero lenta da cui selezionare i dati se voglio selezionare tutte le colonne. Quando io faccio

SELECT TOP 1000 * FROM TABLE

ci vogliono circa 20-25 secondi per leggere i dati dal disco, anche se non impongo alcun ordine sul risultato. Eseguo la query con la cache fredda (cioè dopo DBCC DROPCLEANBUFFERS). Ecco i risultati delle statistiche IO:

Conteggio scansioni 1, letture logiche 364, letture fisiche 24, letture avanti 7191, letture logiche lob 7924, letture fisiche lob 1690, letture read lob 3968.

Raccoglie circa 15 MB di dati. Il piano di esecuzione mostra Scansione indice cluster come mi aspettavo.

Non ci sono operazioni IO sul disco oltre alle mie query; Ho anche verificato che la frammentazione dell'indice cluster sia prossima allo 0%. Questa è un'unità SATA di livello consumer, tuttavia continuerei a pensare che SQL Server sarebbe in grado di scansionare la tabella più velocemente di ~ 100-150 MB / min.

La presenza del campo XML fa sì che la maggior parte dei dati della tabella si trovi nelle pagine LOB_DATA (in effetti ~ 90% delle pagine della tabella sono LOB_DATA).

Immagino che la mia domanda sia: ho ragione nel pensare che le pagine LOB_DATA possono causare scansioni lente non solo a causa delle loro dimensioni, ma anche perché SQL Server non può scansionare efficacemente l'indice cluster quando ci sono molte pagine LOB_DATA nella tabella?

Ancora più in generale: si ritiene ragionevole avere una struttura / tabella di dati di questo tipo? I consigli per l'utilizzo di Filestream in genere indicano dimensioni di campo molto più grandi, quindi non voglio davvero seguire questa strada. Non ho davvero trovato buone informazioni su questo particolare scenario.

Ho pensato alla compressione XML, ma deve essere eseguita sul client o con SQLCLR e richiederebbe un po 'di lavoro da implementare nel sistema.

Ho provato la compressione e poiché gli XML sono estremamente ridondanti, posso (in ac # app) comprimere l'XML da 20 KB a ~ 2,5 KB e archiviarlo nella colonna VARBINARY, impedendo l'uso delle pagine di dati LOB. Questo accelera SELEZIONA 20 volte nei miei test.


Alex: non sono sicuro di aver visto la discussione relativa alla mia risposta (il link è in un commento sotto la mia risposta), ma sono stato in grado di avvicinarmi alla riproduzione del tuo scenario. Ho compilato una tabella corrispondente (tanto quanto avevo informazioni per) la tua descrizione e ho ottenuto statistiche I / O molto simili. Ad eccezione, le "Letture fisiche LOB" non sono mai state neppure vicine. Quindi mi chiedevo se hai aggiornato l'XML (ma non le altre colonne) e / o hai avuto molta frammentazione fisica dei tuoi file di dati. Non mi dispiacerebbe comunque ottenere il DDL della tua tabella e le tue impostazioni di crescita automatica per ogni file di dati e riduci i tuoi file di dati?
Solomon Rutzky,

Prima di tutto - grazie mille per la risposta dettagliata, non sono stato in grado di partecipare alle discussioni in quel momento a causa della mancanza di tempo. Ora che hai menzionato questo (non ci ho pensato quando è stata posta la domanda) - Il campo XML viene aggiornato più volte dopo che è stato creato e creato in piccolo. Quindi sospetto che inizialmente sia archiviato in fila, e dopo alcuni aggiornamenti viene spostato in una struttura di pagina LOB e quindi ottiene altri aggiornamenti.
Alexander Shelemin,

(Continua) Ho verificato la frammentazione fisica dei file prima di porre la domanda e lo strumento Windows incorporato ha ritenuto che fosse OK, quindi non ho esaminato ulteriormente. La crescita automatica è predefinita, di 1 MB, credo, e i file di dati non sono stati ridotti.
Alexander Shelemin,

Selezionare i primi 1000 * è importante nel mio caso particolare. Capisco certamente che è considerata una cattiva pratica, tuttavia alcune decisioni di progettazione delle applicazioni sono davvero difficili da cambiare dopo essere state in atto per molto tempo. Select * viene sostanzialmente utilizzato come strategia di replica tra database tra diversi componenti nella nostra app. Ci sono dei vantaggi, ad esempio possiamo fare molta manipolazione arbitraria con dati / schema al volo, il che sarebbe difficile con le tecniche di replica integrate, ma si presenta con i suoi problemi.
Alexander Shelemin,

Alex, SELECT *non è il problema se hai bisogno dei dati XML. È solo un problema se non vuoi i dati XML, nel qual caso perché rallentare la query per recuperare i dati che non usi? Ho chiesto informazioni sugli aggiornamenti all'XML chiedendomi se la frammentazione nelle pagine LOB non fosse stata riportata in modo accurato. Quale è il motivo per cui avevo chiesto nella mia risposta in che modo hai determinato esattamente che l'indice cluster non era frammentato? Puoi fornire il comando che hai eseguito? E hai fatto una RICOSTRUZIONE completa sull'indice cluster? (continua)
Solomon Rutzky,

Risposte:


11

La presenza del campo XML fa sì che la maggior parte dei dati della tabella si trovi nelle pagine LOB_DATA (in effetti ~ 90% delle pagine della tabella sono LOB_DATA).

Avere semplicemente la colonna XML nella tabella non ha questo effetto. È la presenza di dati XML che, in determinate condizioni , fa sì che una parte dei dati di una riga venga archiviata su una riga, nelle pagine LOB_DATA. E mentre uno (o forse più ;-) potrebbe sostenere che duh, la XMLcolonna implica che ci saranno davvero dati XML, non è garantito che i dati XML debbano essere archiviati off line: a meno che la riga sia praticamente già piena al di fuori del loro essere qualsiasi dato XML, piccoli documenti (fino a 8000 byte) potrebbero adattarsi in fila e non andare mai a una pagina LOB_DATA.

Sono corretto nel pensare che le pagine LOB_DATA possono causare scansioni lente non solo a causa delle loro dimensioni, ma anche perché SQL Server non può scansionare efficacemente l'indice cluster quando ci sono molte pagine LOB_DATA nella tabella?

La scansione si riferisce all'osservazione di tutte le righe. Naturalmente, quando viene letta una pagina di dati, vengono letti tutti i dati in riga , anche se è stato selezionato un sottoinsieme delle colonne. La differenza con i dati LOB è che se non si seleziona quella colonna, i dati fuori riga non verranno letti. Quindi non è proprio corretto trarre una conclusione sull'efficienza con cui SQL Server può eseguire la scansione di questo indice cluster poiché non lo si è esattamente testato (o se ne è stato verificato la metà). Hai selezionato tutte le colonne, che include la colonna XML, e come hai già detto, è lì che si trova la maggior parte dei dati.

Quindi sappiamo già che il SELECT TOP 1000 *test non stava semplicemente leggendo una serie di pagine di dati 8k, tutte in fila, ma saltando invece in altre posizioni per ogni riga . L'esatta struttura di tali dati LOB può variare in base alla loro dimensione. Sulla base della ricerca mostrata qui ( Qual è la dimensione del puntatore LOB per tipi (MAX) come Varchar, Varbinary, Etc? ), Ci sono due tipi di allocazioni LOB off-row:

  1. Root in linea: per dati tra 8001 e 40.000 (in realtà 42.000) byte, spazio consentito, ci saranno da 1 a 5 puntatori (24 - 72 byte) IN RIGA che puntano direttamente alle pagine LOB.
  2. TEXT_TREE: per i dati superiori a 42.000 byte o se i puntatori da 1 a 5 non possono adattarsi in fila, ci sarà solo un puntatore a 24 byte sulla pagina iniziale di un elenco di puntatori alle pagine LOB (ovvero il " pagina "text_tree").

Una di queste due situazioni si verifica ogni volta che si recuperano dati LOB che superano gli 8000 byte o che non si adattano perfettamente alla riga. Ho pubblicato uno script di test su PasteBin.com (script T-SQL per testare le allocazioni e le letture LOB ) che mostra i 3 tipi di allocazioni LOB (in base alla dimensione dei dati) e l'effetto che ciascuno di essi ha su logici e letture fisiche. Nel tuo caso, se i dati XML sono in realtà inferiori a 42.000 byte per riga, nessuno di essi (o molto poco) dovrebbe essere nella struttura TEXT_TREE meno efficiente.

Se si desidera verificare la velocità con cui SQL Server può eseguire la scansione di tale indice cluster, eseguire SELECT TOP 1000ma specificare una o più colonne che non includono quella colonna XML. In che modo ciò influisce sui risultati? Dovrebbe essere un po 'più veloce.

si ritiene ragionevole disporre di una tale struttura di tabella / modello di dati?

Dato che abbiamo una descrizione incompleta dell'attuale struttura della tabella e del modello di dati, qualsiasi risposta potrebbe non essere ottimale a seconda di quali siano quei dettagli mancanti. Con questo in mente, direi che non c'è nulla di ovviamente irragionevole nella struttura della tabella o nel modello di dati.

Posso (in ac # app) comprimere XML da 20 KB a ~ 2,5 KB e archiviarlo nella colonna VARBINARY, impedendo l'utilizzo delle pagine di dati LOB. Questo accelera SELEZIONA 20 volte nei miei test.

Ciò ha reso VARBINARYpiù veloce la selezione di tutte le colonne, o anche solo dei dati XML (ora in ), ma in realtà danneggia le query che non selezionano i dati "XML". Supponendo di avere circa 50 byte nelle altre colonne e di avere un FILLFACTOR100, quindi:

  • Nessuna compressione: 15k di XMLdati dovrebbero richiedere 2 pagine LOB_DATA, che quindi richiedono 2 puntatori per il root inline. Il primo puntatore è di 24 byte e il secondo è 12, per un totale di 36 byte memorizzati in fila per i dati XML. La dimensione totale della riga è di 86 byte e puoi adattare circa 93 di quelle righe a una pagina di dati di 8060 byte. Pertanto, 1 milione di righe richiede 10.753 pagine di dati.

  • Compressione personalizzata: 2,5k di VARBINARYdati si adatteranno in fila. La dimensione totale della riga è di 2610 (2,5 * 1024 = 2560) byte e puoi adattare solo 3 di quelle righe su una pagina di dati da 8060 byte. Pertanto, 1 milione di righe richiede 333.334 pagine di dati.

Ergo, l'implementazione della compressione personalizzata comporta un aumento di 30 volte delle pagine di dati per l'indice cluster. Ciò significa che tutte le query che utilizzano una scansione dell'indice cluster hanno ora circa 322.500 pagine di dati in più da leggere. Si prega di consultare la sezione dettagliata di seguito per ulteriori ramificazioni su come eseguire questo tipo di compressione.

Vorrei mettere in guardia dal fare qualsiasi refactoring basato sulle prestazioni di SELECT TOP 1000 *. Non è probabile che si tratti di una query che l'applicazione emetterà, e non dovrebbe essere utilizzata come unica base per l'ottimizzazione / i potenzialmente inutile.

Per informazioni più dettagliate e altri test da provare, consultare la sezione seguente.


A questa domanda non è possibile dare una risposta definitiva, ma possiamo almeno fare qualche progresso e suggerire ulteriori ricerche per aiutarci ad avvicinarci a capire il problema esatto (idealmente basato su prove).

Quello che sappiamo:

  1. La tabella ha circa 1 milione di righe
  2. La dimensione del tavolo è di circa 15 GB
  3. Tabella contiene una XMLcolonna e diversi altri tipi di colonne: INT, BIGINT, UNIQUEIDENTIFIER, "etc"
  4. XMLla colonna "dimensione" è, in media, circa 15k
  5. Dopo l'esecuzione DBCC DROPCLEANBUFFERS, sono necessari 20-25 secondi per completare la query seguente:SELECT TOP 1000 * FROM TABLE
  6. L'indice cluster è in fase di scansione
  7. La frammentazione sull'indice cluster è vicina allo 0%

Cosa pensiamo di sapere:

  1. Nessun'altra attività su disco al di fuori di queste query. Sei sicuro? Anche se non ci sono altre query degli utenti, ci sono operazioni in background in corso? Esistono processi esterni a SQL Server in esecuzione sullo stesso computer che potrebbero occupare parte dell'IO? Potrebbe non esserci, ma non è chiaro in base esclusivamente alle informazioni fornite.
  2. Vengono restituiti 15 MB di dati XML. Su cosa si basa questo numero? Una stima derivata dalle 1000 righe per la media di 15k di dati XML per riga? O un'aggregazione programmatica di ciò che è stato ricevuto per quella query? Se è solo una stima, non farei affidamento su di essa poiché la distribuzione dei dati XML potrebbe non essere nemmeno nel modo implicito da una media semplice.
  3. La compressione XML potrebbe aiutare. Come faresti esattamente la compressione in .NET? Tramite le classi GZipStream o DeflateStream ? Questa non è un'opzione a costo zero. Comprimerà sicuramente alcuni dei dati di una grande percentuale, ma richiederà anche più CPU in quanto sarà necessario un processo aggiuntivo per comprimere / decomprimere i dati ogni volta. Questo piano eliminerebbe inoltre completamente la tua capacità di:

    • interrogare i dati XML attraverso il .nodes, .value, .query, e .modifyfunzioni XML.
    • indicizzare i dati XML.

      Tieni presente (dal momento che hai menzionato che XML è "altamente ridondante") che il XMLtipo di dati è già ottimizzato in quanto memorizza i nomi degli elementi e degli attributi in un dizionario, assegnando un ID indice intero a ciascun elemento e quindi utilizzando tale ID intero in tutto il documento (quindi non ripete il nome completo per ogni utilizzo, né lo ripete come tag di chiusura per gli elementi). I dati effettivi hanno anche rimosso uno spazio bianco estraneo. Questo è il motivo per cui i documenti XML estratti non mantengono la loro struttura originale e perché gli elementi vuoti vengono estratti <element />anche se inseriti come<element></element>. Pertanto, qualsiasi guadagno derivante dalla compressione tramite GZip (o qualsiasi altra cosa) verrà trovato solo comprimendo i valori di elemento e / o attributo, che è una superficie molto più piccola che potrebbe essere migliorata di quanto molti si aspetterebbero, e molto probabilmente non vale la perdita di capacità come indicato direttamente sopra.

      Tieni inoltre presente che la compressione dei dati XML e la memorizzazione del VARBINARY(MAX)risultato non elimineranno l'accesso a LOB, ma semplicemente lo ridurranno. A seconda della dimensione del resto dei dati nella riga, il valore compresso potrebbe adattarsi nella riga o potrebbe richiedere ancora pagine LOB.

Tali informazioni, sebbene utili, non sono quasi sufficienti. Ci sono molti fattori che influenzano le prestazioni delle query, quindi abbiamo bisogno di un quadro molto più dettagliato di ciò che sta succedendo.

Quello che non sappiamo, ma dobbiamo:

  1. Perché le prestazioni della SELECT *materia? È questo uno schema che usi nel codice. Se è così, perché?
  2. Qual è la prestazione di selezionare solo la colonna XML? Quali sono le statistiche e i tempi se fai solo SELECT TOP 1000 XmlColumn FROM TABLE;:?
  3. Quanto dei 20 - 25 secondi necessari per restituire queste 1000 righe è correlato a fattori di rete (trasmissione dei dati attraverso il cavo) e quanto è correlato a fattori client (il che rende circa 15 MB più il resto dei non- Dati XML nella griglia in SSMS o eventualmente salvando su disco)?

    Il factoring di questi due aspetti dell'operazione a volte può essere fatto semplicemente non restituendo i dati. Ora, si potrebbe pensare di selezionare una tabella temporanea o una variabile di tabella, ma ciò introdurrebbe solo alcune nuove variabili (ad es. I / O del disco per tempdb, scritture del registro delle transazioni, possibile crescita automatica dei dati tempdb e / o file di registro, necessità spazio nel pool di buffer, ecc.). Tutti questi nuovi fattori possono effettivamente aumentare il tempo di query. Invece, in genere memorizzo le colonne in variabili (del tipo di dati appropriato; non SQL_VARIANT) che vengono sovrascritte con ogni nuova riga (cioè SELECT @Column1 = tab.Column1,...).

    TUTTAVIA , come sottolineato da @PaulWhite in questo D & D di DBA.StackExchange, Logical legge diversamente quando si accede agli stessi dati LOB , con ulteriori ricerche del mio postato su PasteBin ( script T-SQL per testare vari scenari per letture LOB ) , LOB non sono accessibili in modo coerente tra SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), e SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Quindi le nostre opzioni sono un po 'più limitate qui, ma ecco cosa si può fare:

    1. Escludere i problemi di rete eseguendo la query sul server che esegue SQL Server, in SSMS o SQLCMD.EXE.
    2. Escludere i problemi dei client in SSMS andando su Opzioni query -> Risultati -> Griglia e selezionando l'opzione "Elimina risultati dopo l'esecuzione". Si noti che questa opzione impedisce TUTTO l'output, inclusi i messaggi, ma può comunque essere utile per escludere il tempo impiegato da SSMS per allocare la memoria per ogni riga e quindi disegnarla nella griglia.
      In alternativa, è possibile eseguire la query tramite sqlcmd.exe e indirizzare l'output di andare da nessuna parte via: -o NUL:.
  4. Esiste un tipo di attesa associato a questa query? Se sì, cos'è quel tipo di attesa?
  5. Qual è la dimensione effettiva dei dati per le XMLcolonne restituite ? La dimensione media di quella colonna sull'intera tabella non ha importanza se le righe "TOP 1000" contengono una porzione sproporzionatamente grande dei XMLdati totali . Se vuoi conoscere le TOP 1000 righe, guarda quelle righe. Si prega di eseguire quanto segue:

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
    
  6. Lo schema esatto della tabella. Fornisci la dichiarazione completa CREATE TABLE , inclusi tutti gli indici.
  7. Piano di query? È qualcosa che puoi pubblicare? Quelle informazioni probabilmente non cambieranno nulla, ma è meglio sapere che non lo farà piuttosto che indovinare che non lo farà e che si sbagliano ;-)
  8. Esiste una frammentazione fisica / esterna sul file di dati? Anche se questo potrebbe non essere un grande fattore qui, dal momento che stai usando "SATA di livello consumer" e non SSD o anche SATA super-costoso, l'effetto dei settori ordinati in modo subottimale sarà più evidente, soprattutto perché il numero di quei settori che deve essere letto aumenta.
  9. Quali sono i risultati esatti della seguente query:

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');
    

AGGIORNARE

Mi è venuto in mente che avrei dovuto provare a riprodurre questo scenario per vedere se ho un comportamento simile. Quindi, ho creato una tabella con diverse colonne (simile alla vaga descrizione nella Domanda), e poi l'ho popolata con 1 milione di righe e la colonna XML ha circa 15k di dati per riga (vedi codice sotto).

Quello che ho scoperto è che fare un SELECT TOP 1000 * FROM TABLEcompletamento completato in 8 secondi la prima volta e 2 - 4 secondi ogni volta in seguito (sì, eseguendo DBCC DROPCLEANBUFFERSprima di ogni esecuzione della SELECT *query). E il mio laptop di diversi anni non è veloce: SQL Server 2012 SP2 Developer Edition, 64 bit, 6 GB RAM, dual 2.5 Ghz Core i5 e un'unità SATA da 5400 RPM. Sto anche eseguendo SSMS 2014, SQL Server Express 2014, Chrome e molte altre cose.

In base al tempo di risposta del mio sistema, ripeterò che abbiamo bisogno di maggiori informazioni (ovvero specifiche sulla tabella e sui dati, risultati dei test suggeriti, ecc.) Al fine di aiutare a restringere la causa del tempo di risposta di 20-25 secondi che stai vedendo.

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

E, poiché vogliamo calcolare il tempo impiegato per leggere le pagine non LOB, ho eseguito la seguente query per selezionare tutto tranne la colonna XML (uno dei test che ho suggerito sopra). Questo ritorna in 1,5 secondi abbastanza coerentemente.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

Conclusione (per il momento)
In base al mio tentativo di ricreare il tuo scenario, non credo che possiamo indicare l'unità SATA o l'I / O non sequenziale come la causa principale dei 20-25 secondi, soprattutto perché ancora non so quanto velocemente la query ritorna quando non si include la colonna XML. E non sono stato in grado di riprodurre il gran numero di Letture logiche (non LOB) che stai mostrando, ma ho la sensazione che ho bisogno di aggiungere più dati a ciascuna riga alla luce di ciò e la dichiarazione di:

~ 90% delle pagine della tabella sono LOB_DATA

La mia tabella ha 1 milione di righe, ognuna con poco più di 15k di dati XML e sys.dm_db_index_physical_statsmostra che ci sono 2 milioni di pagine LOB_DATA. Il restante 10% sarebbe quindi 222k pagine di dati IN_ROW, ma ne ho solo 11.630. Quindi, ancora una volta, abbiamo bisogno di maggiori informazioni sullo schema della tabella attuale e sui dati effettivi.


Questa discussione è stata spostata in chat .
Paul White dice GoFundMonica

10

sono corretto nel pensare che le pagine LOB_DATA possono causare scansioni lente non solo per le loro dimensioni, ma anche perché SQL Server non può scansionare efficacemente l'indice cluster

Sì, la lettura dei dati LOB non memorizzati nella riga porta a IO casuale invece di IO sequenziale. La metrica delle prestazioni del disco da utilizzare qui per capire perché è veloce o lenta è IOPS a lettura casuale.

I dati LOB vengono archiviati in una struttura ad albero in cui la pagina di dati nell'indice cluster punta a una pagina di dati LOB con una struttura radice LOB che a sua volta punta ai dati LOB effettivi. Quando si attraversano i nodi radice nell'indice cluster, SQL Server può ottenere i dati in riga solo mediante letture sequenziali. Per ottenere i dati LOB, SQL Server deve andare altrove sul disco.

Immagino che se passassi a un disco SSD non ne risentiresti molto poiché gli IOPS casuali per un SSD sono molto più alti che per un disco rotante.

si ritiene ragionevole disporre di una tale struttura di tabella / modello di dati?

Sì, potrebbe essere. Dipende da ciò che questa tabella sta facendo per te.

Di solito i problemi di prestazioni con XML in SQL Server si verificano quando si desidera utilizzare T-SQL per eseguire query in XML e ancora di più quando si desidera utilizzare i valori dall'XML in un predicato in una clausola where o join. In tal caso, è possibile dare un'occhiata alla promozione della proprietà o agli indici XML selettivi o a una riprogettazione delle strutture delle tabelle che distruggono l'XML alle tabelle.

Ho provato la compressione

L'ho fatto una volta in un prodotto poco più di 10 anni fa e da allora mi sono pentito. Mi è davvero mancato non poter lavorare con i dati utilizzando T-SQL, quindi non lo consiglierei a nessuno se può essere evitato.


Grazie mille per la risposta. Per quanto riguarda la compressione: non sono sicuro che sia giustificata un'anti-raccomandazione così rigorosa, poiché la necessità di interrogare effettivamente tali dati da T-SQL dipende ovviamente dalla natura dei dati memorizzati. Nel mio caso, ho deciso di andare con la compressione per ora.
Alexander Shelemin,
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.