Ich habe die folgende Tabelle erstellt:
CREATE TABLE dbo.TestStructure
(
id INT NOT NULL,
filler1 CHAR(36) NOT NULL,
filler2 CHAR(216) NOT NULL
);
und erstellte dann einen gruppierten Index:
CREATE CLUSTERED INDEX idx_cl_id
ON dbo.TestStructure(id);
Als nächstes habe ich es mit 30 Zeilen bestückt, jede Größe ist 256 Byte (basierend auf der Tabellendeklaration):
DECLARE @i AS int = 0;
WHILE @i < 30
BEGIN
SET @i = @i + 1;
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (@i, 'a', 'b');
END;
Basierend auf Informationen, die ich im Buch "Training Kit (Prüfung 70-461): Abfragen von Microsoft SQL Server 2012 (Itzik Ben-Gan)" gelesen habe:
SQL Server organisiert intern Daten in einer Datendatei in Seiten. Eine Seite ist eine 8-KB-Einheit und gehört zu einem einzelnen Objekt. Zum Beispiel zu einer Tabelle oder einem Index. Eine Seite ist die kleinste Lese- und Schreibeinheit. Die Seiten sind weiter in Bereiche unterteilt. Ein Umfang besteht aus acht aufeinander folgenden Seiten. Seiten aus einer Ausdehnung können zu einem einzelnen Objekt oder zu mehreren Objekten gehören. Wenn die Seiten zu mehreren Objekten gehören, wird die Ausdehnung als gemischte Ausdehnung bezeichnet. Wenn die Seiten zu einem einzelnen Objekt gehören, wird die Ausdehnung als einheitliche Ausdehnung bezeichnet. SQL Server speichert die ersten acht Seiten eines Objekts in gemischten Ausmaßen. Wenn ein Objekt mehr als acht Seiten umfasst, weist SQL Server diesem Objekt zusätzliche einheitliche Bereiche zu. Mit dieser Organisation verschwenden kleine Objekte weniger Platz und große Objekte sind weniger fragmentiert.
Hier habe ich also die erste 8-KB-Seite mit gemischtem Umfang, die mit 7680 Bytes gefüllt ist (ich habe eine Zeile mit 30 mal 256 Bytes eingefügt, also 30 * 256 = 7680), um die Größe zu überprüfen, die ich beim Größenprüfprozess ausgeführt habe. Es wird das folgende Ergebnis zurückgegeben
index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0
page_count: 1
record_count: 30
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure
rows : 30
reserved : 16 KB
data : 8 KB
index_size : 8 KB
unused : 0 KB
So sind 16 KB für die Tabelle reserviert, die erste 8-KB-Seite ist für die Root-IAM-Seite, die zweite für die Blattdatenspeicherseite, die 8 KB mit einer Belegung von ~ 7,5 KB hat. Jetzt füge ich eine neue Zeile mit 256 Byte ein:
INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');
es wird nicht auf derselben Seite gespeichert, obwohl es einen Speicherplatz von 256 Byte hat (7680 b + 256 = 7936, was immer noch kleiner als 8 KB ist), eine neue Datenseite wird erstellt, aber diese neue Zeile könnte auf dieselbe alte Seite passen Warum erstellt SQL Server eine neue Seite, wenn dies Platz und Zeit spart, wenn Sie sie in die vorhandene Seite einfügen?
Hinweis: Dasselbe passiert im Heap-Index.
quelle
Zwar verwendet SQL Server 8k (8192 Bytes) Datenseiten zum Speichern von 1 oder mehr Zeilen, doch hat jede Datenseite einen gewissen Overhead (96 Bytes) und jede Zeile einen gewissen Overhead (mindestens 9 Bytes). Die 8192 Bytes sind keine reinen Daten.
Für eine genauere Untersuchung, wie dies funktioniert, lesen Sie bitte meine Antwort auf die folgende DBA.SE-Frage:
SUMME der DATALENGTHs, die nicht mit der Tabellengröße von sys.allocation_units übereinstimmen
Anhand der Informationen in dieser verknüpften Antwort erhalten wir ein klareres Bild der tatsächlichen Zeilengröße:
Mit wird
DBCC PAGE
meine Berechnung mitRecord Size 263
(fürtempdb
) undRecord Size 277
(für eine Datenbank, die auf festgelegt istALLOW_SNAPSHOT_ISOLATION ON
) bestätigt.Nun, mit 30 Zeilen, das heißt:
OHNE Versionsinfo
30 * 263 würde uns 7890 Bytes geben. Fügen Sie dann den 96-Byte-Seitenkopf für 7986-Byte hinzu. Fügen Sie abschließend die 60 Bytes (2 pro Zeile) des Slot-Arrays hinzu, sodass insgesamt 8046 Bytes auf der Seite verwendet werden und 146 übrig bleiben. Mit
DBCC PAGE
bestätigen Sie meine Berechnung mit:m_slotCnt 30
(dh Anzahl der Zeilen)m_freeCnt 146
(dh Anzahl der auf der Seite verbleibenden Bytes)m_freeData 7986
(dh Daten + Seitenkopf - 7890 + 96 - Slot-Array wird bei der Berechnung der "verwendeten" Bytes nicht berücksichtigt)MIT Versionsinfo
30 * 277 Bytes für insgesamt 8310 Bytes. Aber 8310 ist über 8192, und das hat nicht einmal den 96-Byte-Seitenkopf oder das 2-Byte-Array pro Zeilen-Slot (30 * 2 = 60 Bytes) berücksichtigt, was uns nur 8036 verwendbare Bytes für die Zeilen geben sollte.
ABER, was ist mit 29 Zeilen? Dies ergibt 8033 Datenbytes (29 * 277) + 96 Bytes für den Seitenkopf + 58 Bytes für das Slot-Array (29 * 2), was 8187 Bytes entspricht. Und das würde die Seite mit 5 verbleibenden Bytes verlassen (8192 - 8187; natürlich unbrauchbar). Mit
DBCC PAGE
bestätigen Sie meine Berechnung mit:m_slotCnt 29
(dh Anzahl der Zeilen)m_freeCnt 5
(dh Anzahl der auf der Seite verbleibenden Bytes)m_freeData 8129
(dh Daten + Seitenkopf - 8033 + 96 - Slot-Array wird bei der Berechnung der "verwendeten" Bytes nicht berücksichtigt)In Bezug auf Haufen
Haufen füllen Datenseiten etwas anders. Sie erhalten eine sehr grobe Schätzung des auf der Seite verbleibenden Speicherplatzes. Wenn an der DBCC - Ausgabe suchen, schauen Sie in der Zeile für:
PAGE HEADER: Allocation Status PFS (1:1)
. Sie werden sehen,VALUE
dass etwas in der Art von0x60 MIXED_EXT ALLOCATED 0_PCT_FULL
(wenn ich die Clustered-Tabelle betrachte) oder0x64 MIXED_EXT ALLOCATED 100_PCT_FULL
wenn ich die Heap-Tabelle betrachte. Dies wird pro Transaktion ausgewertet, sodass einzelne Einfügungen, wie der hier ausgeführte Test, zu unterschiedlichen Ergebnissen zwischen Clustered- und Heap-Tabellen führen können. Wenn Sie jedoch eine einzelne DML-Operation für alle 30 Zeilen ausführen, wird der Heap wie erwartet gefüllt.Keine dieser Details in Bezug auf Heaps wirkt sich jedoch direkt auf diesen speziellen Test aus, da beide Versionen der Tabelle 30 Zeilen mit nur 146 verbleibenden Bytes aufnehmen. Das ist nicht genug Platz für eine andere Zeile, unabhängig von Clustered oder Heap.
Bitte beachten Sie, dass dieser Test ziemlich einfach ist. Die Berechnung der tatsächlichen Größe einer Zeile kann in Abhängigkeit von verschiedenen Faktoren sehr kompliziert werden, z.
SPARSE
B . : , Datenkomprimierung, LOB-Daten usw.Verwenden Sie die folgende Abfrage, um die Details der Datenseite anzuzeigen:
** Der 14-Byte-Wert "Versionsinfo" ist vorhanden, wenn Ihre Datenbank auf "
ALLOW_SNAPSHOT_ISOLATION ON
oder" festgelegt istREAD_COMMITTED_SNAPSHOT ON
.quelle
tempdb
? Ich konnte die genauen Zahlen von OP reproduzieren.ALLOW_SNAPSHOT_ISOLATION
. Ich habe auch gerade versuchttempdb
und gesehen, dass die "Versionsinfo" nicht da ist, daher passen 30 Zeilen. Ich werde aktualisieren, um die neuen Informationen hinzuzufügen.Die eigentliche Struktur der Datenseite ist recht komplex. Während im Allgemeinen angegeben wird, dass 8060 Byte pro Seite für Benutzerdaten verfügbar sind, gibt es einen zusätzlichen Overhead, der nirgendwo gezählt wird, was zu diesem Verhalten führt.
Möglicherweise haben Sie jedoch bemerkt, dass SQL Server Ihnen einen Hinweis gibt, dass die 31. Zeile nicht in die Seite passt. Damit die nächste Zeile auf dieselbe Seite passt, sollte der
avg_page_space_used_in_percent
Wert unter 100% liegen - (100/31) = 96,774194, und in Ihrem Fall liegt er weit darüber.PS: Ich glaube, ich habe in einem der "SQL Server Internals" -Bücher von Kalen Delaney eine ausführliche, bis ins kleinste Byte genaue Erklärung der Datenseitenstruktur gesehen, aber es war fast 10 Jahre her. Außerdem ändert sich die Seitenstruktur in der Regel von Version zu Version.
quelle