Ich habe viele große Tabellen (ungefähr 10 Millionen breite Zeilen), die regelmäßig in SQL Server 2016 geladen werden müssen, um schreibgeschützte Berichte zu erstellen. Ich möchte, dass diese Tabellen auf der Festplatte so klein wie möglich sind, und dies ist wichtiger als Leistungsverbesserungen beim Laden oder Abfragen.
Folgendes habe ich für die Tabellen getan, für die keine weitere Indizierung erforderlich ist:
DATA_COMPRESSION=PAGE
.Die Spaltentypen in den Tabellen sind varchar (niemals mehr als 512, nicht max), float, tinyint oder date (nicht datetime). Alle Spalten werden als nullwertfähig erstellt und es werden keine Primär- oder Fremdschlüssel definiert. Sie spielen für die Abfrage keine Rolle und die Tabellen werden niemals direkt aktualisiert. Die Standardkollatierung für alles ist SQL_Latin1_General_CP1_CI_AS
.
Wenn ich das mache, kann ich in sys.allocation_units
Sehen, dass die Seitendatenkomprimierung auf den Heap angewendet wurde, und ich kann in sys.partitions
Sehen, dass der Füllfaktor korrekt 0 (100%) ist. Da die Tabellen viel kleiner sind als unkomprimierte Tabellen, dachte ich, dass die Komprimierung durchgeführt wurde.
Wenn ich dann aber mit der gleichen Option DATA_COMPRESSION=PAGE
Neu erstelle, wird die angeblich bereits komprimierte Tabelle um 30% kleiner! Es sieht so aus, als würde es von ungefähr 17 Zeilen pro Datenseite auf 25 Zeilen pro Seite gehen. (Nur einmal. Ein erneuter Wiederaufbau macht ihn danach nicht kleiner als beim ersten Wiederaufbau.)
Die Fragen
Meine Fragen sind also: (a) Was ist hier los? und (b) gibt es eine Möglichkeit, diese extra kleine komprimierte Größe direkt beim Laden der Tabelle zu erhalten, ohne sie nach dem Laden der Daten neu erstellen zu müssen?
@ HandyD ist völlig korrekt. Ich möchte nur einige andere Methoden hervorheben, um die Komprimierung beim Einfügen in einen Heap zu erhalten.
Aus demselben Dokument
Wenn ein Heap für die Komprimierung auf Seitenebene konfiguriert ist, erhalten Seiten nur auf folgende Weise eine Komprimierung auf Seitenebene:
- Daten werden mit aktivierten Massenoptimierungen als Massenimport importiert.
- Daten werden mit der Syntax INSERT INTO ... WITH (TABLOCK) eingefügt, und die Tabelle hat keinen nicht gruppierten Index.
- Eine Tabelle wird neu erstellt, indem die Anweisung ALTER TABLE ... REBUILD mit der Komprimierungsoption PAGE ausgeführt wird.
Demnach können Sie minimal protokollierte Masseneinfügungen nutzen oder INSERT INTO ... WITH (TABLOCK)
verwenden, um PAGE
Komprimierung zu erhalten, ohne Neuerstellungen durchführen zu müssen.
(a) Was ist hier los? und (b) gibt es eine Möglichkeit, diese extra kleine komprimierte Größe direkt beim Laden der Tabelle zu erhalten, ohne sie nach dem Laden der Daten neu erstellen zu müssen?
Es gibt Regeln, um beim Einfügen in einen Heap die Komprimierung PAGE
zu erhalten. Fügen Sie Ihrem Befehl bcp
-h "TABLOCK"
Hinzu, um die Komprimierung zu erhalten.
ROW
Die Komprimierung funktioniert ohne diese Voraussetzungen und ist die geringste Komprimierungsmenge, die in den folgenden Beispielen verwendet wird. Vielen Dank @ DenisRubashkin für den Hinweis!
Beispiel für einen Startdaten- und BCP-Out-Befehl
--Tested on SQL Server 2014 SP2
CREATE TABLE dbo.CompressedHeap_Source( Val varchar(512),
Datefield Date,
Tinyfield TinyINT,
Floatfield float)
WITH (DATA_COMPRESSION = PAGE);
INSERT INTO dbo.CompressedHeap_Source
(
Val,Datefield,Tinyfield,Floatfield)
SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;
--bcp TEST.dbo.CompressedHeap_Source out E:\Data\HeapData.bcp -c -T
Die komprimierte und unkomprimierte Größe ROW
Die Datengröße liegt bei 132272 KB
Bei einer Standardeinfügung in den Heap. Dies ist ROW
komprimiert, aber nicht PAGE
komprimiert.
Die Datengröße ohne Komprimierung beträgt für unseren Test ~ 176216 KB
.
exec sp_spaceused 'dbo.CompressedHeap_Source'
name rows reserved data index_size unused
CompressedHeap_Source 6365530 132296 KB 132272 KB 8 KB 16 KB
INSERT IN ... WITH TABLOCK
Durch Einfügen von WITH TABLOCK
Erhalten wir die komprimierte Datengröße PAGE
69480 KB
.
INSERT INTO dbo.CompressedHeap_Source2 WITH(TABLOCK)
(
Val,Datefield,Tinyfield,Floatfield)
SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2
BULK INSERT
Wenn wir nun eine Ziel-Heap-Tabelle erstellen, die ebenfalls page
komprimiert ist, und eine Masseneinfügung with tablock
Durchführen:
CREATE TABLE dbo.CompressedHeap_Destination( Val varchar(512),
Datefield Date,
Tinyfield TinyINT,
Floatfield float)
WITH (DATA_COMPRESSION = PAGE);
bulk insert dbo.CompressedHeap_Destination
from 'E:\Data\HeapData.bcp' with (TABLOCK)
Die Daten werden page
komprimiert und befinden sich ebenfalls bei 69480 KB
:
name rows reserved data index_size unused
CompressedHeap_Destination 6365530 69512 KB 69480 KB 8 KB 24 KB
BCP IN MIT TABLOCK
Sie können die gleichen Ergebnisse wie beim BULK INSERT WITH TABLOCK
Erhalten, indem Sie BCP IN
Mit dem Hinweis -h "TABLOCK"
Verwenden. Das macht Sinn, sie machen das auch intern
--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T -h "TABLOCK"
Die resultierende Größe ist 69480 KB
BCP IN OHNE TABLOCK
Verwenden von BCP zum Laden von Daten aus derselben Datei in eine Kopie der Zieltabelle
Ein Standard-Befehl bcp führt zu nicht komprimierten Daten:
--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T
Mit der Datengröße bei 132272 KB
(Zeile komprimiert).
Laut dem Artikel Docs über Komprimierung:
Neue Seiten, die im Rahmen von DML-Vorgängen in einem Heap zugewiesen werden, verwenden die PAGE-Komprimierung erst, wenn der Heap neu erstellt wurde. Erstellen Sie den Heap neu, indem Sie die Komprimierung entfernen und erneut anwenden oder einen Clustered-Index erstellen und entfernen.
Dies scheint mit dem übereinzustimmen, was Sie sehen. Es scheint, als würden Sie erst dann eine Komprimierung auf dem Tisch erhalten, wenn Sie ihn neu erstellen. Sie können versuchen, die Daten in eine nicht komprimierte Tabelle zu laden und festzustellen, ob Sie durchschnittlich noch 17 Zeilen pro Seite haben oder ob dies abnimmt. Wenn es gleich bleibt, erhalten Sie keine Komprimierung und die Neuerstellung ist erforderlich.
Sie können Ihrer Tabelle auch einen Clustered-Index hinzufügen. Dies sollte verhindern, dass Ihre Tabelle nach dem Massenladen Ihrer Daten dekomprimiert/niedrig komprimiert wird.