Warum kann der Datenraum einer Tabelle das Vierfache der Größe der Rohdaten in Anspruch nehmen?


18

Ich habe eine Tabelle mit 490 M Zeilen und 55 GB Tabellenbereich, also ca. 167 Bytes pro Zeile. Die Tabelle enthält drei Spalten: a VARCHAR(100), a DATETIME2(0)und a SMALLINT. Die durchschnittliche Länge des Textes in dem VARCHARFeld beträgt ungefähr 21,5, daher sollten die Rohdaten ungefähr 32 Bytes pro Zeile betragen: 22 + 2 für die Ganzzahl VARCHAR, 6 für die DATETIME2Ganzzahl und 2 für die 16-Bit-Ganzzahl.

Beachten Sie, dass das oben stehende Leerzeichen nur Daten und keine Indizes sind. Ich verwende den unter Eigenschaften | gemeldeten Wert Lagerung | Allgemein | Datenraum.

Natürlich muss es etwas Overhead geben, aber 135 Bytes pro Zeile scheinen viel zu sein, besonders für eine große Tabelle. Warum könnte das so sein? Hat noch jemand ähnliche Multiplikatoren gesehen? Welche Faktoren können den zusätzlichen Platzbedarf beeinflussen?

Zum Vergleich habe ich versucht, eine Tabelle mit zwei INTFeldern und 1 M Zeilen zu erstellen . Der erforderliche Datenraum betrug 16,4 MB: 17 Byte pro Zeile im Vergleich zu 8 Byte Rohdaten. Eine andere Testtabelle mit einem INTund VARCHAR(100)mit dem gleichen Text wie die reale Tabelle belegt 39 Bytes pro Zeile (44 K Zeilen), wobei ich 28 plus ein wenig erwarten würde.

Der Produktionstisch hat also erheblich mehr Overhead. Liegt das daran, dass es größer ist? Ich würde erwarten, dass die Indexgrößen ungefähr N * log (N) betragen, aber ich verstehe nicht, warum der für die tatsächlichen Daten erforderliche Speicherplatz nicht linear ist.

Vielen Dank im Voraus für alle Hinweise!

BEARBEITEN:

Alle aufgeführten Felder sind NOT NULL. Die reale Tabelle hat eine gruppierte PK auf dem VARCHARFeld und dem DATETIME2Feld in dieser Reihenfolge. Für die beiden Tests war der erste INTder (gruppierte) PK.

Wenn es darauf ankommt: Die Tabelle ist eine Aufzeichnung der Ping-Ergebnisse. Die Felder sind URL, Ping-Datum / Uhrzeit und Latenz in Millisekunden. Daten werden ständig angehängt und nie aktualisiert, aber die Daten werden regelmäßig gelöscht, um sie auf wenige Datensätze pro Stunde und URL zu reduzieren.

BEARBEITEN:

Eine sehr interessante Antwort hier , dass schon sagt, für einen Index mit viel Lesen und Schreiben, Wiederaufbau kann nicht von Vorteil sein. In meinem Fall ist der belegte Speicherplatz ein Problem, aber wenn die Schreibleistung wichtiger ist, kann es sein, dass man mit schlaffen Indizes besser dran ist.

Antworten:


11

Nach Diskussionen in den Kommentaren zur ursprünglichen Frage scheint es in diesem Fall, dass der verlorene Speicherplatz durch die Wahl des gruppierten Schlüssels verursacht wird, was zu einer massiven Fragmentierung geführt hat.

In diesen Situationen lohnt es sich immer, den Fragmentierungsstatus über sys.dm_db_index_physical_stats zu überprüfen.

Bearbeiten: Nach Aktualisierung in Kommentaren

Die durchschnittliche Seitendichte (vor der Neuerstellung des Clustered-Index) betrug 24%, was perfekt zur ursprünglichen Frage passt. Die Seiten waren nur zu 1/4 voll, sodass die Gesamtgröße das Vierfache der Rohdatengröße betrug.


7

Die On-Disk-Strukturen haben Overhead:

  • Zeilenkopf
  • Null-Bitmap + Zeiger
  • Spaltenversätze variabler Länge
  • Zeilenversionszeiger (optional)
  • ...

Nehmen Sie 2 x 4 Bytes in Spalten, haben Sie

  • 4 Byte Zeilenheader
  • 2-Byte-Zeiger auf NULL-Bitmap
  • 8 Bytes für 2 int Spalten
  • 3 Byte NULL-Bitmap

Wow 17 Bytes!

Sie können dasselbe für Ihren zweiten Testtisch tun, der mehr Overhead hat als Ihr ursprünglicher:

  • 2 Byte für die Anzahl der Spalten variabler Länge
  • 2 Bytes pro Spalte mit variabler Länge

Warum der Unterschied? Außerdem (ich werde nicht auf diese verlinken)

  • Haben Sie jemals Indizes neu erstellt, um sie zu defragmentieren?
  • Löschvorgänge beanspruchen keinen Speicherplatz
  • Datenseiten werden geteilt, wenn Sie in die Mitte einfügen
  • Aktualisierungen können Vorwärtszeiger verursachen (Lücke lassen)
  • Zeilenüberlauf
  • Die varchar-Spalte wurde ohne Indexwiederherstellung oder DBCC CLEANTABLE entfernt
  • Heap oder Tabelle (Heap hat keinen gruppierten Index = überall verstreute Datensätze)
  • RCSI-Isolationsstufe (zusätzliche 14 Byte pro Zeile)
  • Leerzeichen am Ende (SET ANSI_PADDING ist standardmäßig auf ON gesetzt) ​​in varchar. Verwenden Sie DATALENGTH zum Überprüfen, nicht LEN
  • Führen Sie sp_spaceused mit aus @updateusage = 'true'
  • ...

Siehe hierzu: SQL Server: Wie erstelle ich eine Tabelle, die eine 8-KB-Seite füllt?

Von SO:


Das 2x4 Byte Int-Spaltenmuster ist nicht 100% korrekt. Sie haben einen 4-Byte-Zeilenkopf (2 Statusbytes und 2 Bytes für die Datengröße mit fester Länge). Dann haben Sie 2x4 Bytes für die Daten. Zwei Bytes für die Spaltenanzahl und ein einzelnes Byte für die Null-Bitmap ergeben eine Gesamtdatensatzlänge von 15 Bytes, nicht von 17.
Mark S. Rasmussen,

@ Mark S. Rasmussen: Woher bekommen Sie "2 Bytes für die Datengröße mit fester Länge"? MSDN? Die Null-Bitmap ist immer 3 Byte groß : sqlskills.com/blogs/paul/post/… + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
gbn

Wow, tolles Detail! Ich habe das Längenfeld der VARCHARs in meiner obigen Schätzung berücksichtigt , aber nicht die Anzahl der Spalten. Diese Tabelle hat keine NULL-fähigen Felder (hätte das erwähnen sollen), weist sie ihnen immer noch Bytes zu?
Jon of All Trades

Würden sich Neuerstellungsindizes auf den Datenteil des erforderlichen Speicherplatzes auswirken ? Vielleicht würde der Clustered-Index neu erstellt. Einfügungen passieren häufig in der Mitte, wenn ich die Reihenfolge der Cluster-Felder vertauschte, die aufhören würden. Die meisten anderen sollten in diesem Fall nicht zutreffen, aber es ist eine gute Referenz für den allgemeinen Fall. Ich werde Ihre Links überprüfen. Gutes Zeug!
Jon of All Trades

1
@gbn Die 2 Bytes für die Datengröße mit fester Länge sind Teil des 4-Byte-Zeilenkopfs, den Sie erwähnen. Dies ist der Zeiger, der auf das Ende des Teils fester Datenlänge / Beginn der Spaltenanzahl / Null-Bitmap zeigt. Die NULL-Bitmap besteht nicht immer aus drei Bytes. Wenn Sie die Spaltenanzahl angeben, beträgt diese mindestens drei Byte, möglicherweise jedoch mehr. Ich habe die Bitmap und die Spaltenanzahl in meiner Beschreibung aufgeteilt. Außerdem ist die NULL-Bitmap nicht immer vorhanden, obwohl dies in diesem Fall der Fall ist.
Mark S. Rasmussen

5

Haben sich die Datentypen im Laufe der Zeit geändert? Wurden Spalten mit variabler Länge entfernt? Wurden die Indizes häufig defragmentiert, aber nie neu erstellt? Wurden viele Zeilen gelöscht oder wurden viele Spalten mit variabler Länge erheblich aktualisiert? Einige gute Diskussionen hier .


Ich bin zu 97% sicher, dass ich keinen Datentyp geändert oder ein Feld entfernt habe. Wenn ich das getan hätte, wäre es sehr früh gewesen, als die Tabelle weit weniger Zeilen hatte. Es gibt keine Löschungen oder Aktualisierungen, Daten werden immer nur angehängt.
Jon of All Trades

Korrektur: Es gibt Löschungen und ziemlich viel. Der Tisch hat ein beachtliches Nettowachstum, daher würde ich mir vorstellen, dass dieser Platz schnell wiederverwendet wird.
Jon of All Trades

Bei vielen Löschungen können die Daten wiederverwendet werden oder nicht. Was ist der Clustering-Schlüssel der Tabelle? Befinden sich Einfügungen in der Mitte des Tisches oder am Ende?
Mrdenny

Die gruppierten Schlüssel ist , Verbindung, auf den VARCHARund DATETIME2Felder, in dieser Reihenfolge. Die Einfügungen werden für das erste Feld gleichmäßig verteilt. Für das zweite Feld werden neue Werte und immer größer als alle vorhandenen.
Jon of All Trades
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.