LOB_DATA, langsame Tabellenscans und einige E / A-Fragen

19

Ich habe eine ziemlich große Tabelle mit einer der Spalten, die XML-Daten mit einer durchschnittlichen Größe des XML-Eintrags von ~ 15 Kilobyte sind. Alle anderen Spalten sind reguläre Ints, Bigints, GUIDs usw. Um einige konkrete Zahlen zu haben, nehmen wir an, die Tabelle hat eine Million Zeilen und ist ~ 15 GB groß.

Was mir aufgefallen ist, dass diese Tabelle nur sehr langsam Daten auswählt, wenn ich alle Spalten auswählen möchte. Wenn ich es tue

SELECT TOP 1000 * FROM TABLE

Es dauert ungefähr 20-25 Sekunden, um die Daten von der Festplatte zu lesen - obwohl ich dem Ergebnis keine Reihenfolge auferlege. Ich starte die Abfrage mit dem kalten Cache (dh nach DBCC DROPCLEANBUFFERS). Hier sind die IO-Statistikergebnisse:

Scan-Anzahl 1, logische Lesevorgänge 364, physikalische Lesevorgänge 24, Vorauslesevorgänge 7191, logische Lobs-Lesevorgänge 7924, physikalische Lobs-Lesevorgänge 1690, Lobs-Vorauslesevorgänge 3968.

Es werden ca. 15 MB Daten erfasst. Der Ausführungsplan zeigt den erwarteten Clustered Index Scan.

Auf der Festplatte ist außer meinen Abfragen kein E / A-Vorgang aktiv. Ich habe auch überprüft, dass die Fragmentierung des Clustered-Index nahe bei 0% liegt. Dies ist ein SATA-Laufwerk für Endverbraucher. Ich würde jedoch immer noch glauben, dass SQL Server in der Lage ist, die Tabelle schneller als ~ 100-150 MB / min zu scannen.

Das Vorhandensein eines XML-Felds bewirkt, dass sich die meisten Tabellendaten auf LOB_DATA-Seiten befinden (tatsächlich sind ~ 90% der Tabellenseiten LOB_DATA).

Ich denke, meine Frage lautet: Stimmt es, dass LOB_DATA-Seiten langsame Scans verursachen können, nicht nur aufgrund ihrer Größe, sondern auch, weil SQL Server den Clustered-Index nicht effektiv scannen kann, wenn viele LOB_DATA-Seiten in der Tabelle enthalten sind?

Noch umfassender: Wird eine solche Tabellenstruktur / ein solches Datenmuster als sinnvoll angesehen? Empfehlungen für die Verwendung von Filestream enthalten normalerweise viel größere Felder, sodass ich diesen Weg nicht wirklich einschlagen möchte. Ich habe keine guten Informationen zu diesem bestimmten Szenario gefunden.

Ich habe über eine XML-Komprimierung nachgedacht, diese muss jedoch auf dem Client oder mit SQLCLR durchgeführt werden und erfordert einige Arbeit, um sie im System zu implementieren.

Ich habe die Komprimierung ausprobiert, und da XMLs hochgradig redundant sind, kann ich XML (in einer ac # -App) von 20 KB auf ~ 2,5 KB komprimieren und in der Spalte VARBINARY speichern, wodurch die Verwendung von LOB-Datenseiten verhindert wird. Dies beschleunigt SELECTs in meinen Tests um das 20-fache.

Alexander Shelemin
quelle
Alex: Ich bin mir nicht sicher, ob Sie die Diskussion zu meiner Antwort gesehen haben (der Link befindet sich in einem Kommentar unter meiner Antwort), aber ich konnte Ihr Szenario fast reproduzieren. Ich habe eine Tabelle ausgefüllt, die Ihrer Beschreibung entspricht (so viele Informationen ich hatte), und E / A-Statistiken erhalten, die sehr ähnlich sind. Bis auf die Tatsache, dass die "LOB Physical Reads" noch nie in der Nähe waren. Ich habe mich also gefragt, ob Sie das XML (aber nicht die anderen Spalten) aktualisiert haben und / oder ob Ihre Datendateien physisch stark fragmentiert sind. Es würde mir immer noch nichts ausmachen, die DDL Ihrer Tabelle und die Einstellung für das automatische Wachstum für jede Datendatei abzurufen. Verkleinern Sie Ihre Datendateien?
Solomon Rutzky
Zunächst einmal - vielen Dank für die ausführliche Antwort, ich konnte zu der Zeit aus Zeitgründen nicht an der Diskussion teilnehmen. Nun, da Sie dies erwähnt haben (ich habe bei der Frage nicht daran gedacht) - Das XML-Feld wird nach seiner Erstellung mehrmals aktualisiert und ist klein. Daher würde ich vermuten, dass es anfänglich in Reihe gespeichert wird und nach einigen Aktualisierungen in eine LOB-Seitenstruktur verschoben wird und dann einige weitere Aktualisierungen erhält.
Alexander Shelemin
(Fortsetzung) Ich habe die physische Fragmentierung von Dateien überprüft, bevor ich die Frage gestellt habe, und das integrierte Windows-Tool fand es in Ordnung, sodass ich mich nicht weiter damit befasste. Das automatische Wachstum ist meiner Meinung nach standardmäßig um 1 MB erhöht, und die Datendateien wurden nicht verkleinert.
Alexander Shelemin
Select Top 1000 * ist in meinem speziellen Fall von Bedeutung. Ich verstehe mit Sicherheit, dass dies eine schlechte Vorgehensweise ist. Einige Entscheidungen zum Anwendungsdesign sind jedoch nach langer Zeit nur schwer zu ändern. Select * wird grundsätzlich als datenbankübergreifende Replikationsstrategie zwischen verschiedenen Komponenten in unserer App verwendet. Es gibt Profis, zum Beispiel können wir eine Menge willkürlicher Manipulationen mit Daten / Schemata im laufenden Betrieb durchführen, was mit den eingebauten Replikationstechniken schwierig wäre, aber es bringt Probleme mit sich.
Alexander Shelemin
Alex, SELECT *ist nicht das Problem, wenn Sie die XML-Daten benötigen. Es ist nur dann ein Problem, wenn Sie die XML-Daten nicht benötigen. In diesem Fall sollten Sie die Abfrage verlangsamen, um nicht verwendete Daten zurückzugewinnen. Ich fragte nach den Aktualisierungen des XML-Codes und fragte mich, ob die Fragmentierung auf den LOB-Seiten nicht korrekt gemeldet wurde. Aus diesem Grund hatte ich in meiner Antwort gefragt, wie genau Sie festgestellt haben, dass der Clustered-Index nicht fragmentiert ist. Können Sie den Befehl bereitstellen, den Sie ausgeführt haben? Und haben Sie den Clustered Index vollständig überarbeitet? (Fortsetzung)
Solomon Rutzky

Antworten:

11

Das Vorhandensein eines XML-Felds bewirkt, dass sich die meisten Tabellendaten auf LOB_DATA-Seiten befinden (tatsächlich sind ~ 90% der Tabellenseiten LOB_DATA).

Nur die XML-Spalte in der Tabelle zu haben, hat diesen Effekt nicht. Aufgrund des Vorhandenseins von XML- Daten wird unter bestimmten Umständen ein Teil der Daten einer Zeile außerhalb der Zeile auf LOB_DATA-Seiten gespeichert. Und während einer (oder vielleicht mehrere ;-) argumentieren, dass duh, XMLimpliziert die Spalte, dass es tatsächlich XML-Daten geben wird, kann nicht garantiert werden, dass die XML-Daten außerhalb der Zeile gespeichert werden müssen: es sei denn, die Zeile ist so gut wie bereits gefüllt Abgesehen davon, dass es sich um XML-Daten handelt, passen kleine Dokumente (bis zu 8000 Byte) möglicherweise in eine Zeile und werden niemals zu einer LOB_DATA-Seite weitergeleitet.

Stimmt es, dass LOB_DATA-Seiten nicht nur aufgrund ihrer Größe langsame Scans verursachen können, sondern auch, weil SQL Server den Clustered-Index nicht effektiv scannen kann, wenn viele LOB_DATA-Seiten in der Tabelle enthalten sind?

Das Scannen bezieht sich auf das Betrachten aller Zeilen. Wenn eine Datenseite gelesen wird, werden natürlich alle In-Row- Daten gelesen, auch wenn Sie eine Teilmenge der Spalten ausgewählt haben. Der Unterschied zu LOB-Daten besteht darin, dass die Daten außerhalb der Zeile nicht gelesen werden, wenn Sie diese Spalte nicht auswählen. Daher ist es nicht wirklich fair, eine Schlussfolgerung darüber zu ziehen, wie effizient SQL Server diesen Clustered Index scannen kann, da Sie dies nicht genau getestet haben (oder die Hälfte davon getestet haben). Sie haben alle Spalten ausgewählt, einschließlich der XML-Spalte. Wie Sie bereits erwähnt haben, befinden sich dort die meisten Daten.

Wir wissen also bereits, dass der SELECT TOP 1000 *Test nicht nur eine Reihe von 8.000 Datenseiten in einer Reihe las, sondern stattdessen zu anderen Stellen pro Reihe sprang . Die genaue Struktur dieser LOB-Daten kann je nach Größe variieren. Basierend auf den hier gezeigten Untersuchungen ( Wie groß ist der LOB-Zeiger für (MAX) -Typen wie Varchar, Varbinary usw.? ) Gibt es zwei Arten von Off-Row-LOB-Zuweisungen:

  1. Inline Root - für Daten zwischen 8001 und 40.000 (wirklich 42.000) Bytes, sofern der Speicherplatz dies zulässt, gibt es 1 bis 5 Zeiger (24 - 72 Bytes) IN ROW, die direkt auf die LOB-Seite (n) verweisen.
  2. TEXT_TREE - für Daten über 42.000 Bytes oder wenn die 1 bis 5 Zeiger nicht in die Zeile passen, gibt es nur einen 24-Byte-Zeiger auf die Startseite einer Liste von Zeigern auf die LOB-Seiten (dh die " text_tree "Seite).

Eine dieser beiden Situationen tritt jedes Mal auf, wenn Sie LOB-Daten abrufen, die größer als 8000 Bytes sind oder einfach nicht in die Zeile passen. Ich habe ein Testskript auf PasteBin.com veröffentlicht ( T-SQL-Skript zum Testen von LOB-Zuweisungen und -Lesevorgängen ), in dem die drei Arten von LOB-Zuweisungen (basierend auf der Größe der Daten) sowie deren Auswirkungen auf logische und angezeigt werden physische liest. Wenn in Ihrem Fall die XML-Daten tatsächlich weniger als 42.000 Byte pro Zeile umfassen, sollte sich keine (oder nur sehr wenige) davon in der am wenigsten effizienten TEXT_TREE-Struktur befinden.

Wenn Sie testen wollen , wie schnell SQL Server scannen , dass Clustered Index, tun das SELECT TOP 1000aber geben Sie eine oder mehrere Spalten nicht mit dieser XML - Spalte. Wie wirkt sich das auf Ihre Ergebnisse aus? Es sollte ziemlich viel schneller sein.

Wird es als sinnvoll erachtet, eine solche Tabellenstruktur / ein solches Datenmuster zu haben?

Da wir eine unvollständige Beschreibung der tatsächlichen Tabellenstruktur und des Datenmusters haben, ist eine Antwort möglicherweise nicht optimal, abhängig von den fehlenden Details. In diesem Sinne würde ich sagen, dass Ihre Tabellenstruktur oder Ihr Datenmuster offensichtlich nichts Unangemessenes ist.

Ich kann (in ac # app) XML von 20 KB auf ~ 2,5 KB komprimieren und in der Spalte VARBINARY speichern, wodurch die Verwendung von LOB-Datenseiten verhindert wird. Dies beschleunigt SELECTs in meinen Tests um das 20-fache.

Das hat das Auswählen aller Spalten oder sogar nur der XML-Daten (jetzt in VARBINARY) beschleunigt, aber es schadet tatsächlich Abfragen, bei denen die "XML" -Daten nicht ausgewählt werden. Angenommen, Sie haben ungefähr 50 Bytes in den anderen Spalten und haben einen FILLFACTORWert von 100, dann:

  • Keine Komprimierung: Für XML15 KB Daten sollten 2 LOB_DATA-Seiten erforderlich sein, für die dann 2 Zeiger für den Inline-Stamm erforderlich sind. Der erste Zeiger ist 24 Bytes und der zweite 12 Bytes für insgesamt 36 Bytes, die in Reihe für die XML-Daten gespeichert sind. Die Gesamtzeilengröße beträgt 86 Byte, und Sie können ungefähr 93 dieser Zeilen auf eine 8060-Byte-Datenseite einfügen. Daher erfordert 1 Million Zeilen 10.753 Datenseiten.

  • Benutzerdefinierte Komprimierung: 2,5 KB VARBINARYDaten passen in eine Zeile. Die Gesamtzeilengröße beträgt 2610 (2,5 * 1024 = 2560) Byte, und Sie können nur 3 dieser Zeilen auf eine 8060-Byte-Datenseite einfügen. Daher erfordert 1 Million Zeilen 333.334 Datenseiten.

Ergo führt die Implementierung einer benutzerdefinierten Komprimierung zu einer 30-fachen Erhöhung der Datenseiten für den Clustered Index. Das bedeutet, dass bei allen Abfragen, die einen Clustered-Index-Scan verwenden, jetzt etwa 322.500 weitere Datenseiten zu lesen sind. Weitere Informationen zu dieser Art der Komprimierung finden Sie im folgenden Abschnitt.

Ich würde davor warnen, Refactoring auf der Grundlage der Leistung von durchzuführen SELECT TOP 1000 *. Dies ist wahrscheinlich keine Abfrage, die von der Anwendung selbst ausgegeben wird, und sollte nicht als einzige Grundlage für möglicherweise unnötige Optimierungen verwendet werden.

Weitere Informationen und Tests finden Sie im folgenden Abschnitt.


Diese Frage kann nicht definitiv beantwortet werden, aber wir können zumindest einige Fortschritte erzielen und zusätzliche Untersuchungen vorschlagen, um uns dem genauen Problem zu nähern (im Idealfall basierend auf Beweisen).

Was wir wissen:

  1. Die Tabelle enthält ungefähr 1 Million Zeilen
  2. Die Tabellengröße beträgt ca. 15 GB
  3. Tabelle enthält eine XMLSpalte und mehrere andere Spalten der Typen: INT, BIGINT, UNIQUEIDENTIFIER, „etc.“
  4. XMLDie "Spaltengröße" beträgt im Durchschnitt ca. 15 KB
  5. Nach der Ausführung DBCC DROPCLEANBUFFERSdauert es 20 bis 25 Sekunden, bis die folgende Abfrage abgeschlossen ist:SELECT TOP 1000 * FROM TABLE
  6. Der Clustered Index wird gescannt
  7. Fragmentierung auf dem Clustered Index liegt nahe bei 0%

Was wir zu wissen glauben:

  1. Keine andere Festplattenaktivität außerhalb dieser Abfragen. Bist du sicher? Gibt es Hintergrundoperationen, auch wenn keine anderen Benutzerabfragen vorliegen? Gibt es Prozesse außerhalb von SQL Server, die auf demselben Computer ausgeführt werden, auf dem möglicherweise ein Teil der E / A ausgeführt wird? Es kann sein, dass dies nicht der Fall ist, aber es ist nicht klar, allein basierend auf den bereitgestellten Informationen.
  2. Es werden 15 MB XML-Daten zurückgegeben. Worauf basiert diese Zahl? Eine Schätzung, die aus den 1000 Zeilen mal dem Durchschnitt von 15.000 XML-Daten pro Zeile abgeleitet wurde? Oder eine programmatische Zusammenfassung dessen, was für diese Abfrage empfangen wurde? Wenn es sich nur um eine Schätzung handelt, würde ich mich nicht darauf verlassen, da die Verteilung der XML-Daten möglicherweise nicht so ist, wie es ein einfacher Durchschnitt impliziert.
  3. XML-Komprimierung könnte helfen. Wie genau würden Sie die Komprimierung in .NET durchführen? Über die Klassen GZipStream oder DeflateStream ? Dies ist keine kostenfreie Option. Es wird sicherlich einige der Daten um einen großen Prozentsatz komprimieren, aber es wird auch mehr CPU erfordern, da Sie jedes Mal einen zusätzlichen Prozess zum Komprimieren / Dekomprimieren der Daten benötigen. Mit diesem Plan können Sie auch Folgendes vollständig entfernen:

    • Abfrage der XML - Daten über die .nodes, .value, .queryund .modifyXML - Funktionen.
    • Indizieren Sie die XML-Daten.

      Beachten Sie (da Sie erwähnt haben, dass XML "hochredundant" ist), dass der XMLDatentyp bereits dahingehend optimiert ist, dass er die Element- und Attributnamen in einem Wörterbuch speichert, jedem Element eine ganzzahlige Index-ID zuweist und dann diese ganzzahlige ID verwendet im gesamten Dokument (daher wiederholt es nicht den vollständigen Namen für jede Verwendung, noch wiederholt es ihn erneut als abschließendes Tag für Elemente). Bei den eigentlichen Daten wurden auch externe Leerzeichen entfernt. Dies ist der Grund, warum extrahierte XML-Dokumente ihre ursprüngliche Struktur nicht beibehalten und warum leere Elemente so extrahiert werden, als <element />ob sie als eingefügt worden wären<element></element>. Alle Vorteile der Komprimierung über GZip (oder etwas anderes) werden also nur durch die Komprimierung der Element- und / oder Attributwerte erzielt. Dies ist eine viel kleinere Oberfläche, die verbessert werden könnte, als die meisten dies erwarten würden und deren Verlust sich wahrscheinlich nicht lohnt Fähigkeiten wie direkt oben angegeben.

      Beachten Sie bitte auch, dass durch das Komprimieren der XML-Daten und das Speichern des VARBINARY(MAX)Ergebnisses der LOB-Zugriff nicht aufgehoben, sondern nur reduziert wird. Abhängig von der Größe der restlichen Daten in der Zeile passt der komprimierte Wert möglicherweise in die Zeile oder erfordert weiterhin LOB-Seiten.

Diese Informationen sind zwar hilfreich, aber bei weitem nicht genug. Es gibt eine Reihe von Faktoren, die die Abfrageleistung beeinflussen. Daher benötigen wir ein detaillierteres Bild der aktuellen Ereignisse.

Was wir nicht wissen, aber müssen:

  1. Warum ist die Leistung von SELECT *Materie? Ist dies ein Muster, das Sie im Code verwenden. Wenn ja warum?
  2. Was ist die Leistung beim Auswählen nur der XML-Spalte? Was sind die Statistiken und das Timing, wenn Sie nur Folgendes tun SELECT TOP 1000 XmlColumn FROM TABLE;:?
  3. Wie viel von den 20 bis 25 Sekunden, die für die Rückgabe dieser 1000 Zeilen benötigt werden, hängt mit den Netzwerkfaktoren (Abrufen der Daten über die Leitung) zusammen und wie viel mit den Client-Faktoren (Rendern von ca. 15 MB plus dem Rest der nicht zur Verfügung stehenden Daten). XML-Daten in das Grid in SSMS (oder möglicherweise auf Festplatte speichern)?

    Das Ausklammern dieser beiden Aspekte des Vorgangs kann manchmal erfolgen, indem die Daten einfach nicht zurückgegeben werden. Man könnte nun überlegen, eine temporäre Tabelle oder eine Tabellenvariable auszuwählen, aber dies würde nur ein paar neue Variablen einführen (z. B. Datenträger-E / A für tempdbTransaktionsprotokoll-Schreibvorgänge, mögliches automatisches Wachstum von Tempdb-Daten und / oder Protokolldateien) Platz im Buffer Pool, etc). All diese neuen Faktoren können die Abfragezeit tatsächlich verlängern. Stattdessen speichere ich die Spalten normalerweise in Variablen (des entsprechenden Datentyps; nicht SQL_VARIANT), die mit jeder neuen Zeile (dh SELECT @Column1 = tab.Column1,...) überschrieben werden .

    JEDOCH , wie von @PaulWhite in diesem DBA.StackExchange Q & A wies darauf hin, Logik liest anders , wenn die gleichen LOB - Daten zugreifen , mit zusätzlicher Forschung meiner eigenen geschrieben auf Pastebin ( T-SQL - Skript verschiedene Szenarien zu testen , für LOB liest ) , LOB konsistent zwischen nicht zugegriffen SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), und SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Daher sind unsere Optionen hier etwas eingeschränkt, aber hier ist, was getan werden kann:

    1. Schließen Sie Netzwerkprobleme aus, indem Sie die Abfrage auf dem Server ausführen, auf dem SQL Server ausgeführt wird, entweder in SSMS oder in SQLCMD.EXE.
    2. Schließen Sie Client-Probleme in SSMS aus, indem Sie unter Abfrageoptionen -> Ergebnisse -> Raster die Option "Ergebnisse nach Ausführung verwerfen" aktivieren. Bitte beachten Sie, dass diese Option ALLE Ausgaben, einschließlich Nachrichten, verhindert, aber dennoch nützlich sein kann, um die Zeit auszuschließen, die SSMS benötigt, um den Speicher für jede Zeile zuzuweisen und ihn dann in das Raster zu zeichnen.
      Alternativ können Sie die Abfrage über sqlcmd.exe ausführen und die Ausgabe zu nirgendwo gehen direkt über: -o NUL:.
  4. Ist dieser Abfrage ein Wartetyp zugeordnet? Wenn ja, welcher Wartetyp ist das?
  5. Was ist die tatsächliche Datengröße für die XMLSpalten zurückgegeben werden ? Die durchschnittliche Größe dieser Spalte in der gesamten Tabelle spielt keine Rolle, wenn die "TOP 1000" -Zeilen einen unverhältnismäßig großen Teil der Gesamtdaten XMLenthalten. Wenn Sie mehr über die TOP 1000-Zeilen erfahren möchten, schauen Sie sich diese Zeilen an. Bitte führen Sie Folgendes aus:

    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. Das genaue Tabellenschema. Bitte geben Sie die vollständige CREATE TABLE Erklärung einschließlich aller Indizes an.
  7. Abfrageplan? Kannst du das posten? Diese Information wird wahrscheinlich nichts ändern, aber es ist besser zu wissen, dass dies nicht der Fall ist, als zu erraten, dass dies nicht der Fall ist und falsch ist ;-)
  8. Befindet sich in der Datendatei eine physische / externe Fragmentierung? Obwohl dies hier möglicherweise kein großer Faktor ist, da Sie "Consumer-Grade-SATA" und nicht SSD oder sogar Super-Expensive-SATA verwenden, wird der Effekt von nicht optimal geordneten Sektoren stärker spürbar sein, insbesondere hinsichtlich der Anzahl dieser Sektoren das muss gelesen werden erhöht.
  9. Was sind die genauen Ergebnisse der folgenden Abfrage:

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

AKTUALISIEREN

Mir fiel ein, dass ich versuchen sollte, dieses Szenario zu reproduzieren, um festzustellen, ob ich ein ähnliches Verhalten habe. Also habe ich eine Tabelle mit mehreren Spalten erstellt (ähnlich der vagen Beschreibung in der Frage) und sie dann mit 1 Million Zeilen aufgefüllt. Die XML-Spalte enthält ungefähr 15 KB Daten pro Zeile (siehe Code unten).

Was ich gefunden habe, ist, dass SELECT TOP 1000 * FROM TABLEdas erste Mal in 8 Sekunden und danach jedes Mal 2 - 4 Sekunden abgeschlossen wurden (ja, wird DBCC DROPCLEANBUFFERSvor jedem Durchlauf der SELECT *Abfrage ausgeführt). Und mein mehrere Jahre alter Laptop ist nicht schnell: SQL Server 2012 SP2 Developer Edition, 64-Bit, 6 GB RAM, dualer 2,5-GHz-Core i5 und ein SATA-Laufwerk mit 5400 U / min. Ich verwende auch SSMS 2014, SQL Server Express 2014, Chrome und einige andere Dinge.

Aufgrund der Reaktionszeit meines Systems möchte ich wiederholen, dass wir weitere Informationen benötigen (z. B. Angaben zu Tabelle und Daten, Ergebnisse der vorgeschlagenen Tests usw.), um die Ursache für die Reaktionszeit von 20 bis 25 Sekunden einzugrenzen dass du siehst.

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.
*/

Und weil wir die Zeit herausrechnen möchten, die zum Lesen der Nicht-LOB-Seiten benötigt wird, habe ich die folgende Abfrage ausgeführt, um alle außer der XML-Spalte auszuwählen (einer der oben vorgeschlagenen Tests). Dies kehrt ziemlich konsequent in 1,5 Sekunden zurück.

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.
*/

Fazit (für den Moment)
Aufgrund meines Versuchs, Ihr Szenario neu zu erstellen, können wir weder auf das SATA-Laufwerk noch auf nicht-sequentielle E / A als Hauptursache für die 20 bis 25 Sekunden verweisen, vor allem, weil wir dies immer noch tun Ich weiß nicht, wie schnell die Abfrage zurückkehrt, wenn die XML-Spalte nicht enthalten ist. Und ich war nicht in der Lage, die große Anzahl von Logical Reads (Nicht-LOB) zu reproduzieren, die Sie anzeigen, aber ich habe das Gefühl, dass ich in Anbetracht dessen und der folgenden Aussage mehr Daten zu jeder Zeile hinzufügen muss :

~ 90% der Tabellenseiten sind LOB_DATA

Meine Tabelle enthält 1 Million Zeilen mit jeweils etwas mehr als 15.000 XML-Daten und sys.dm_db_index_physical_statszeigt, dass es 2 Millionen LOB_DATA-Seiten gibt. Die restlichen 10% wären dann 222.000 IN_ROW-Datenseiten, von denen ich jedoch nur 11.630 habe. Wir brauchen also noch einmal mehr Informationen über das aktuelle Tabellenschema und die aktuellen Daten.

Solomon Rutzky
quelle
Diese Diskussion wurde in den Chat verschoben .
Paul White sagt GoFundMonica
10

Bin ich richtig in der Annahme, dass LOB_DATA-Seiten nicht nur aufgrund ihrer Größe langsame Scans verursachen können, sondern auch, weil SQL Server den Clustered-Index nicht effektiv scannen kann

Ja, das Lesen von LOB-Daten, die nicht in Reihe gespeichert sind, führt zu zufälligen E / A-Vorgängen anstelle von sequentiellen E / A-Vorgängen. Die hier zu verwendende Kennzahl für die Festplattenleistung, um zu verstehen, warum sie schnell oder langsam ist, ist Random Read IOPS.

LOB-Daten werden in einer Baumstruktur gespeichert, in der die Datenseite im Clustered-Index auf eine LOB-Datenseite mit einer LOB-Stammstruktur verweist, die wiederum auf die tatsächlichen LOB-Daten verweist. Beim Durchlaufen der Stammknoten im Clustered-Index kann SQL Server die In-Row-Daten nur durch sequentielle Lesevorgänge abrufen. Um die LOB-Daten zu erhalten, muss SQL Server an einer anderen Stelle auf der Festplatte abgelegt werden.

Ich schätze, wenn Sie auf eine SSD-Festplatte wechseln würden, würden Sie nicht so stark darunter leiden, da zufällige IOPS für eine SSD viel höher sind als für eine sich drehende Festplatte.

Wird es als sinnvoll erachtet, eine solche Tabellenstruktur / ein solches Datenmuster zu haben?

Ja, das könnte sein. Kommt darauf an, was dieser Tisch für Sie tut.

In der Regel treten die Leistungsprobleme bei XML in SQL Server auf, wenn Sie T-SQL zum Abfragen in XML verwenden möchten, und noch mehr, wenn Sie Werte aus XML in einem Prädikat in einer where-Klausel oder einem Join verwenden möchten. Wenn das der Fall ist , könnten Sie einen Blick auf haben Eigenschaftenheraufstufung oder selektiven XML - Indizes oder ein Redesign Ihrer Tabellenstrukturen , anstatt die XML - Tabellen Schredder.

Ich habe die Komprimierung ausprobiert

Ich habe das vor etwas mehr als 10 Jahren einmal in einem Produkt getan und es seitdem bereut. Ich habe wirklich vermisst, dass ich mit T-SQL nicht mit den Daten arbeiten kann, deshalb würde ich das niemandem empfehlen, wenn es vermieden werden kann.

Mikael Eriksson
quelle
Vielen Dank für die Antwort. Bezüglich der Komprimierung: Ich bin mir nicht sicher, ob eine so strenge Anti-Empfehlung gerechtfertigt ist, da die Notwendigkeit, diese Daten von T-SQL tatsächlich abzufragen, offensichtlich von der Art der gespeicherten Daten abhängt. In meinem Fall habe ich mich für die Komprimierung entschieden.
Alexander Shelemin