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 entiretablecache
und 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 NULL
Werte, sie werden entweder als leere Zeichenfolge 1900-01-01
oder 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.
quelle
Antworten:
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:
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.
Strukturaufwand
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
FILLFACTOR
Wert 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.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).CHAR
/VARCHAR
data wird mit 1 Byte pro Zeichen gespeichert (Doppelbyte-Zeichen werden momentan ignoriert).XML
ist 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 wieNCHAR
/ alle UTF-16 (2 oder 4 Byte pro "Zeichen")NVARCHAR
.DATETIME2
liegt zwischen 6 und 8 Bytes.DECIMAL
liegt zwischen 5 und 17 Bytes (abhängig von der Genauigkeit).Strings (ebenfalls unter der Annahme von .NET) sind immer UTF-16. Es gibt keine Optimierung für 8-Bit-Zeichenfolgen wie das, was
VARCHAR
gilt. 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).XML
kann oder kann nicht auf die gleiche Weise im Speicher gespeichert werden (ich muss das nachschlagen).DateTime
8 Byte ist immer (wie T-SQLDATETIME
, aber nicht wieDATE
,TIME
oderDATETIME2
).Decimal
ist 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):
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 ).
quelle
Aus Ihrer Frage geht hervor, dass Sie eine maximale Cache-Größe haben
S
und 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 istS
. 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:
sys.partitions
die Größe der Tabelle mithilfe von Spaltendefinitionen anzeigen und berechnen.BIGINT
Spalten die Datengröße 10000000 * (8 + 8 + 8 + 8 + 8) = 400 MByte haben, was größer sein könnte als Ihre Cache-GrößenbeschränkungS
. Es spielt keine Rolle, ob es auch eine Reihe von Zeichenfolgenspalten hat.BIGINT
Spalte und einerNVARCHAR(20)
Spalte darf beispielsweise 100 * (8 + 2 * 20) = 4800 Byte nicht überschreiten.S
, äußerst unwahrscheinlich in den Cache passt. Sie müssten testen, um herauszufinden, ob ein solcher Wert vorhanden ist.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,
S
oder 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:
TABLESAMPLE
, solange Ihre Stichprobe groß genug ist.SUM()
, die aufgrund des Werts dieses Aggregats vorzeitig beendet wird. Ich habe diese Arbeit nur für gesehenROW_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.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.
quelle