Ermitteln Sie die unkomprimierte Größe aller Tabellen in einer Datenbank

12

In Dynamics AX gibt es einen Caching-Mechanismus, mit dem Tabellen so konfiguriert werden können, dass sie in den Speicher geladen und zwischengespeichert werden. Dieser Cache ist auf eine bestimmte Menge an KB beschränkt, um Speicherprobleme zu vermeiden. Die Einstellung, von der ich spreche, wird aufgerufen entiretablecacheund lädt die gesamte Tabelle in den Speicher, sobald ein einzelner Datensatz angefordert wird.

Bis vor kurzem haben wir uns auf einige Skripte verlassen, um die Größe der Tabellen mit dieser Einstellung zu überprüfen, um festzustellen, ob die Tabellengröße über dieser Grenze liegt.

Jetzt kommt jedoch die Komprimierung ins Spiel und Dinge wie sp_spaceused oder sys.allocation_units scheinen den von den komprimierten Daten tatsächlich verwendeten Speicherplatz zu melden.

Offensichtlich arbeitet der Anwendungsserver mit nicht komprimierten Daten, sodass die Datengröße auf der Festplatte in SQL Server keine Rolle spielt. Ich benötige die tatsächliche Größe der unkomprimierten Daten.

Ich kenne sp_estimate_data_compression_savings, aber wie der Name schon sagt, ist dies nur eine Schätzung.
Ich würde es vorziehen, wenn die Größe so korrekt wie möglich wäre.

Die einzige Möglichkeit, die mir in den Sinn kam, war ein komplexes dynamisches SQL, das nicht komprimierte Tabellen mit derselben Struktur wie die komprimierten Tabellen erstellt, die komprimierten Daten in diese Schattentabelle einfügt und dann die Größe dieser Schattentabelle überprüft.
Das ist natürlich etwas langwierig und es dauert eine Weile, bis die Datenbank mehrere Hundert GB umfasst.

Powershell könnte eine Option sein, aber ich möchte nicht über alle Tabellen iterieren, um eine select *Überprüfung der Größe im Skript durchzuführen, da dies nur den Cache überfluten und wahrscheinlich auch viel Zeit in Anspruch nehmen würde.

Kurz gesagt, ich brauche eine Möglichkeit, die Größe für jede Tabelle zu ermitteln, da diese einmal dekomprimiert und aus der Gleichung, die der Anwendung vorgelegt wird, fragmentiert wird, sofern dies möglich ist. Ich bin offen für verschiedene Ansätze, T-SQL wird bevorzugt, aber ich bin nicht gegen Powershell oder andere kreative Ansätze.

Angenommen, der Puffer in der Anwendung entspricht der Größe der Daten. Ein Bigint ist immer so groß wie ein Bigint, und ein Zeichendatentyp ist 2 Byte pro Zeichen (Unicode). BLOB-Daten haben auch die Größe der Daten, eine Aufzählung ist im Grunde ein Int und numerische Daten sind numerisch (38,12), datetime ist die Größe einer datetime. Es gibt auch keine NULLWerte, sie werden entweder als leere Zeichenfolge 1900-01-01oder als Null gespeichert .

Es gibt keine Dokumentation darüber, wie dies implementiert wird, aber die Annahmen basieren auf einigen Tests und den von PFE und dem Support-Team verwendeten Skripten (die anscheinend auch die Komprimierung ignorieren, da die Prüfung in der Anwendung erstellt wurde und die App dies nicht feststellen kann Wenn die zugrunde liegenden Daten komprimiert sind, überprüfen Sie auch die Tabellengrößen. Dieser Link zum Beispiel lautet:

Vermeiden Sie die Verwendung von EntireTable-Caches für große Tabellen (in AX 2009 über 128 KB oder 16 Seiten, in AX 2012 über die Anwendungseinstellung "Gesamte Tabellencachegröße" [Standard: 32 KB oder 4 Seiten]). Wechseln Sie stattdessen zum Aufzeichnungscaching.

Tom V - versuchen Sie topanswers.xyz
quelle
3
Es ist hacky, aber vielleicht wäre eine wiederhergestellte Kopie mit deaktivierter Komprimierung die genaueste. Dann testen Sie auch Wiederherstellungen, wodurch Sie wie ein TOP 1-Datenbankadministrator aussehen.
Erik Darling
Glauben Sie, das wäre Ihre beste Wahl. Es könnte Möglichkeiten geben, die Mathematik zu versuchen. Wie viele Zeilen mit definierten spaltenweisen Datentypen und Längen multipliziert werden, addieren sich dann in den Indizes usw. Es ist viel mehr Arbeit als das Skript für die Wiederherstellung und das Deaktivieren der Komprimierung, das @sp_BlitzErik oben vorschlägt. Und wer möchte nicht TOP 1 DBA werden?
Mike Walsh
SUM (Datenlänge ()) für alle Spalten erhalten unkomprimierte Datengröße?
Tapakah Ua
@sp_BlitzErik Das könnte eine Antwort anstelle eines Kommentars sein.
Tom V - versuchen Sie topanswers.xyz

Antworten:

7

Ich benötige die tatsächliche Größe der unkomprimierten Daten.
...
Ich würde es vorziehen, wenn die Größe so korrekt wie möglich wäre.

Obwohl der Wunsch nach diesen Informationen durchaus verständlich ist, ist es schwieriger, diese Informationen zu erhalten, insbesondere im Zusammenhang mit "so richtig wie möglich", als dies aufgrund fehlerhafter Annahmen von jedermann erwartet wird. Unabhängig davon, ob Sie die in der Frage erwähnte unkomprimierte Schattentabelle verwenden oder @ sp_BlitzEriks Vorschlag in einem Kommentar zur Wiederherstellung der Datenbank und zur Überprüfung der Dekomprimierung, sollte nicht davon ausgegangen werden, dass die Größe der unkomprimierten Tabelle der Größe der Daten im Speicher entspricht auf dem app server:

  1. Werden alle Zeilen in der Tabelle zwischengespeichert? Oder nur in Reichweite? Die Annahme hier ist, dass es alles ist, und das könnte richtig sein, aber ich dachte, es sollte zumindest erwähnt werden, dass dies möglicherweise nicht der Fall ist (sofern in der Dokumentation nichts anderes angegeben ist, aber dies ist sowieso ein kleiner Punkt, der einfach nicht gewünscht ist es ist nicht zu erwähnen).

    Die Frage wurde aktualisiert und lautet: Ja, alle Zeilen werden zwischengespeichert.

  2. Strukturaufwand

    1. Auf der DB-Seite:
      Seite und Zeilen-Overhead auf der DB-Seite: Wie viele Zeilen auf eine Seite passen, hängt von vielen Faktoren ab, die Schätzungen beeinträchtigen können. Selbst bei einem FILLFACTORWert von 100 (oder 0) verbleibt wahrscheinlich noch nicht verwendeter Speicherplatz auf der Seite, da dies nicht für eine ganze Zeile ausreicht. Und das zusätzlich zum Seitenkopf. Wenn eine Snapshot-Isolationsfunktion aktiviert ist, werden meines Erachtens zusätzliche 13 Bytes pro Zeile von der Versionsnummer belegt, was Schätzungen zunichte macht. Es gibt noch weitere Kleinigkeiten in Bezug auf die tatsächliche Größe der Zeile (NULL-Bitmap, Spalten variabler Länge usw.), aber die bisher genannten Punkte sollten allein ausschlaggebend sein.
    2. Auf dem App-Server:
      Welche Art von Sammlung wird zum Speichern der zwischengespeicherten Ergebnisse verwendet? Ich nehme an, dies ist eine .NET-App, also ist es eine DataTable? Eine generische Liste? Ein SortedDictionary? Jede Art von Sammlung hat eine unterschiedliche Menge an belauschten. Ich würde nicht erwarten, dass eine der Optionen den Seiten- und Zeilen-Overhead auf der DB-Seite unbedingt spiegelt, insbesondere bei der Skalierung (ich bin sicher, dass eine kleine Menge von Zeilen nicht genügend verschiedene Elemente enthält, um eine Rolle zu spielen, aber Sie suchen nicht nach Unterschieden in Hunderten von Bytes oder nur ein paar kB).
  3. Datentypen
    1. Auf der DB-Seite:
      CHAR/ VARCHARdata wird mit 1 Byte pro Zeichen gespeichert (Doppelbyte-Zeichen werden momentan ignoriert). XMList optimiert, um nicht annähernd so viel Platz einzunehmen, wie die Textdarstellung implizieren würde. Dieser Datentyp erstellt ein Wörterbuch mit Element- und Attributnamen und ersetzt die tatsächlichen Verweise auf diese im Dokument durch ihre jeweiligen IDs (eigentlich ganz nett). Andernfalls lauten die Zeichenfolgenwerte wie NCHAR/ alle UTF-16 (2 oder 4 Byte pro "Zeichen") NVARCHAR. DATETIME2liegt zwischen 6 und 8 Bytes. DECIMALliegt zwischen 5 und 17 Bytes (abhängig von der Genauigkeit).
    2. Auf der Seite des App-Servers:
      Strings (ebenfalls unter der Annahme von .NET) sind immer UTF-16. Es gibt keine Optimierung für 8-Bit-Zeichenfolgen wie das, was VARCHARgilt. ABER, Strings können auch "interniert" werden. Hierbei handelt es sich um eine gemeinsam genutzte Kopie, auf die häufig verwiesen werden kann (aber ich weiß nicht, ob dies für Strings in Sammlungen funktioniert oder wenn ja, ob es für alle Arten von Sammlungen funktioniert). XMLkann oder kann nicht auf die gleiche Weise im Speicher gespeichert werden (ich muss das nachschlagen). DateTime8 Byte ist immer (wie T-SQL DATETIME, aber nicht wie DATE, TIMEoder DATETIME2). Decimalist immer 16 Bytes .

Das alles, um zu sagen: Es gibt so ziemlich nichts, was Sie auf der DB-Seite tun können, um selbst auf der Seite des App-Servers eine ziemlich genaue Größe des Speicherbedarfs zu erreichen. Sie müssen eine Möglichkeit finden, den App-Server selbst abzufragen, nachdem er mit einer bestimmten Tabelle geladen wurde. Und ich bin mir nicht sicher, ob ein Debugger die Laufzeitgröße einer gefüllten Sammlung anzeigen würde. Wenn dies nicht der Fall ist, können Sie nur alle Zeilen einer Tabelle durchgehen und jede Spalte mit der entsprechenden .NET- Größe (z. B. INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃 usw.) multiplizieren. Damit bleibt jedoch die Frage offen des Overheads der Sammlung plus jedes Element der Sammlung.

Angesichts einer neuen Definition in der Frage könnte man wahrscheinlich die folgende Abfrage durchführen, um näher heranzukommen. Dabei spielt es keine Rolle, ob die Tabelle komprimiert ist oder nicht. Es ist jedoch Sache der einzelnen Personen, zu bestimmen, ob das Scannen aller Zeilen in der Produktion angemessen ist (möglicherweise nach einer Wiederherstellung oder außerhalb der Spitzenzeiten):

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

Denken Sie jedoch daran, dass dies keinen Auflistungs- oder Auflistungselement-Overhead berücksichtigt. Und nicht sicher, ob wir diesen Wert ohne einen Debugger erhalten können (oder möglicherweise etwas wie ILSpy, aber ich empfehle das nicht, da es die EULA in Abhängigkeit von den lokalen Gesetzen verletzen könnte ).

Solomon Rutzky
quelle
Am Ende haben wir die Codeprüfungen implementiert, um sicherzustellen, dass die Puffergröße so ist, wie sie der Anwendung präsentiert wird.
Tom V - versuchen Sie topanswers.xyz
6

Aus Ihrer Frage geht hervor, dass Sie eine maximale Cache-Größe haben Sund keine Tabellen in den Cache laden möchten, die diese Größe überschreiten. Wenn dies zutrifft, müssen Sie nicht die genaue Größe jeder Tabelle kennen. Sie müssen nur wissen, ob eine Tabelle größer oder kleiner als die maximale Cachegröße ist S. Abhängig von den Spaltendefinitionen und der Zeilenanzahl Ihrer Tabellen ist dies ein erheblich einfacheres Problem.

Ich stimme der großartigen Antwort von Solomon Rutzky darin zu, dass das Betrachten von unkomprimierten Daten nicht der richtige Weg ist und es möglicherweise schwierig ist, eine gute Näherung für die wahre Größe einer Tabelle im Cache zu finden. Ich werde jedoch im Rahmen der Frage arbeiten und davon ausgehen, dass Sie eine Formel entwickeln können, die auf Spaltendefinitionen für statische Datentypen und der tatsächlichen Länge Ihrer dynamischen Spalten basiert.

Wenn Sie diese Zuordnung von Datentypen zur Cache-Größe haben, sollten Sie in der Lage sein, einige Tabellen auszuwerten, ohne sich die Daten in ihnen anzusehen:

  1. Wenn eine Tabelle nur statische Datentypen enthält (keine Zeichenfolgen oder Blobs), können Sie die Anzahl der Zeilen approximieren, indem Sie sys.partitionsdie Größe der Tabelle mithilfe von Spaltendefinitionen anzeigen und berechnen.
  2. Wenn eine Tabelle mit vielen Zeilen über genügend statische Datentypspalten verfügt, können Sie sie möglicherweise als zu groß entfernen, ohne ihre Daten zu überprüfen. Beispielsweise könnte eine Tabelle mit 10 Millionen Zeilen und 5 BIGINTSpalten die Datengröße 10000000 * (8 + 8 + 8 + 8 + 8) = 400 MByte haben, was größer sein könnte als Ihre Cache-Größenbeschränkung S. Es spielt keine Rolle, ob es auch eine Reihe von Zeichenfolgenspalten hat.
  3. Wenn eine Tabelle mit wenigen Zeilen klein genug ist, können Sie möglicherweise bestätigen, dass sie unter dem Grenzwert liegt, indem Sie einfach davon ausgehen, dass jeder dynamische Datentyp die maximal mögliche Größe hat. Eine 100-Zeilen-Tabelle mit einer BIGINTSpalte und einer NVARCHAR(20)Spalte darf beispielsweise 100 * (8 + 2 * 20) = 4800 Byte nicht überschreiten.
  4. Es kann sein, dass eine Tabelle, die in SQL Server eine komprimierte Größe aufweist, die um einen Faktor größer ist S, äußerst unwahrscheinlich in den Cache passt. Sie müssten testen, um herauszufinden, ob ein solcher Wert vorhanden ist.
  5. Sie können sich glücklich schätzen, dass alle dynamischen Spalten statistische Daten enthalten. Statistiken enthalten Informationen über die durchschnittliche Länge, die für Ihre Zwecke möglicherweise genau genug sind.

Möglicherweise müssen Sie die Daten von Tabellen abfragen, die keinem der oben genannten Kriterien entsprechen. Es gibt einige Tricks, mit denen Sie die Auswirkungen auf die Leistung minimieren können. Ich würde sagen, dass Sie hier zwei konkurrierende Prioritäten haben: Sie legen Wert auf Genauigkeit, möchten aber nicht alle Daten in Ihrer Datenbank scannen. Möglicherweise können Sie Ihren Berechnungen eine Art Puffer hinzufügen. Ich weiß nicht, ob es akzeptabler ist, eine Tabelle auszuschließen, die geringfügig unter der maximalen Cachegröße von liegt, Soder eine Tabelle aufzunehmen, die geringfügig über der maximalen Cachegröße liegt.

Im Folgenden finden Sie einige Vorschläge, wie Sie die Abfragen für Tabellendaten beschleunigen können:

  1. Für große Tabellen können Sie möglicherweise verwenden TABLESAMPLE, solange Ihre Stichprobe groß genug ist.
  2. Bei großen Tabellen mit einem gruppierten Schlüssel kann es hilfreich sein, diese stapelweise auf dem gruppierten Schlüssel zu verarbeiten. Leider kenne ich keine Methode, um eine zu berechnen SUM(), die aufgrund des Werts dieses Aggregats vorzeitig beendet wird. Ich habe diese Arbeit nur für gesehen ROW_NUMBER(). Sie können jedoch die ersten 10% der Tabelle scannen, die berechnete Datengröße reduzieren, die nächsten 10% scannen und so weiter. Bei Tabellen, die für den Cache zu groß sind, können Sie mit dieser Methode möglicherweise viel Arbeit sparen, indem Sie sie vorzeitig beenden.
  3. Bei einigen Tabellen haben Sie möglicherweise das Glück, über verdeckende Indizes für alle dynamischen Spalten zu verfügen. Abhängig von der Zeilengröße oder anderen Faktoren kann das Durchsuchen jedes Indexes schneller sein als das Durchsuchen einer Tabelle. Sie können diesen Vorgang auch vorzeitig beenden, wenn die Tabellengröße nach dem Lesen eines Indexes für eine einzelne Spalte zu groß ist.
  4. Die durchschnittliche Länge Ihrer dynamischen Spalten ändert sich im Laufe der Zeit möglicherweise nicht sehr stark. Es kann sinnvoll sein, die berechneten Durchschnittslängen zu sparen und diese Werte für eine Weile in Ihren Berechnungen zu verwenden. Sie können diese Werte basierend auf der DML-Aktivität in den Tabellen oder basierend auf einer anderen Metrik zurücksetzen.
  5. Wenn es möglich ist, Tests für alle Tabellen durchzuführen, um einen Algorithmus zu entwickeln, können Sie möglicherweise Muster in den Daten nutzen. Wenn Sie beispielsweise Tabellen verarbeiten, die mit der kleinsten beginnen, stellen Sie möglicherweise fest, dass nach der Verarbeitung von 10 (ich habe diese Nummer zusammengestellt) Tabellen in einer Zeile, die für den Cache zu groß sind, kaum größere Tabellen in die Tabelle passen Zwischenspeicher. Dies kann akzeptabel sein, wenn es in Ordnung ist, einige Tabellen auszuschließen, die möglicherweise in den Cache passen könnten.

Mir ist klar, dass ich in dieser Antwort keinen SQL-Code angegeben habe. Lassen Sie mich wissen, ob es hilfreich wäre, Demo-Code für eine der hier diskutierten Ideen zu schreiben.

Joe Obbish
quelle
2
Ich hatte nicht daran gedacht
, solche