Verwendet SQL Server Zeiger, anstatt doppelte Zeilen zu speichern?

8

Ich verwende die integrierte sp_spaceusedgespeicherte Prozedur vor und nach dem Ausführen einer Operation in unserer Software, um zu sehen, welche Tabellen Zeileneinfügungen enthalten und wie sich die Größe jeder Tabelle ändert.

Was ich sehe, ist, dass von allen Tabellen, in die Zeilen geschrieben werden, nur eine Handvoll zeigt, dass die Tabelle seitlich vergrößert wurde. Die anderen, die Zeilen anzeigen, die hinzugefügt wurden, zeigen keine Änderung der Größe dieser gespeicherten Prozedur.

Der einzige Fall, in dem dies nicht zutrifft, ist bei der ersten Transaktion, nachdem alle Tabellen abgeschnitten wurden. Mir scheint also, dass SQL Server beim Speichern doppelter Daten anzeigt, dass Zeilen eingefügt wurden, sondern nur Zeiger auf vorherige identische Zeilen speichern müssen.

Kann jemand dies bitte bestätigen?

Dan Revell
quelle
Markiert für dba.se
gbn

Antworten:

13

Nein, SQL Server erkennt keine doppelten Zeilen

SQL Server füllt leere oder teilweise leere Seiten innerhalb der zugewiesenen Seiten.

Wenn ich also eine sehr schmale Zeile habe (sagen wir 2 Spalten), kann ich ein paar hundert weitere Zeilen auf derselben Seite hinzufügen, ohne den verwendeten Speicherplatz zu erhöhen.

Schnelle und schmutzige Demo (ohne doppelte Zeilen, aber Sie können damit spielen, wenn Sie möchten)

IF OBJECT_ID('dbo.Demo') IS NOT NULL
    DROP TABLE dbo.Demo;
GO
CREATE TABLE dbo.Demo (DemoID int NOT NULL IDENTITY(1,1), Demo char(1) NOT NULL)
GO
SELECT 'zero rows, zero space', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

INSERT dbo.Demo VALUES ('a');
GO
SELECT 'one row. Peanuts', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

INSERT dbo.Demo VALUES ('b');
GO 100
SELECT '101 rows. All on one page', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

INSERT dbo.Demo VALUES ('b');
GO 1899
SELECT '2000 rows. More than one page', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

TRUNCATE TABLE dbo.Demo
GO
SELECT 'zero rows, zero space. TRUNCATE deallocates pages', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

INSERT dbo.Demo VALUES ('c');
GO 500
SELECT '500 rows. Some space used', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

DELETE dbo.Demo
GO
SELECT 'zero rows after delete. Space still allocated', SUM(ps.reserved_page_count)/128.0 AS ReservedMB, SUM(ps.used_page_count)/128.0 AS UsedMB
FROM sys.dm_db_partition_stats ps
WHERE ps.object_id = OBJECT_ID('dbo.Demo')
GO

IF OBJECT_ID('dbo.Demo') IS NOT NULL
    DROP TABLE dbo.Demo;
GO
gbn
quelle
Danke, das ist eine wundervolle Antwort. Ich wünschte, ich könnte mehr abstimmen. Vielleicht könnten Sie auch vorschlagen, wenn Sie so freundlich wären, wie ich die tatsächlich auf der Seite verwendeten Daten berechnen könnte. Alles, was ich sehen kann, ist: Namenszeilen reservierte Daten index_size unbenutzte Gebühren 6 16 KB 8 KB 8 KB 0 KB Daraus kann ich nicht ersehen, wie viel von der Seite ich für meine 6 Zeilen verwende. Es sagt mir, dass ich 0 KB auf dieser Seite nicht verwendet habe, obwohl ich weiß, dass dies nicht der Fall ist.
Ich habe DBCC SHOWCONTIG ausprobiert, aber dies zeigt keine großen Spalten an, die so wie ich es verstehe im LOB gespeichert sind.
Ich dachte, ich würde eher kommentieren als eine neue Frage erstellen ... Wie funktioniert das Speichern breiterer Tabellen? Was passiert, wenn ich zum Beispiel eine Tabelle habe, die wirklich breit ist, aber meistens 60% der Spalten null sind? Ich gehe davon aus, dass diese Zeile dieselbe Menge an Speicherplatz auf der Seite benötigt, da diese Spalten Daten enthalten KÖNNTEN . Nur in Bezug auf die Speicherung (alles kann natürlich zu wörtlich genommen werden) ist es besser, mehr Tabellen zu haben, die eng sind? Wenn Sie die "leeren" Spalten ohnehin häufig einlesen müssen, ist es wahrscheinlich sinnvoll, dies zusammen mit der Haupttabelle beizubehalten?
Bdwakefield
7

Verwendet SQL Server Zeiger, anstatt doppelte Zeilen zu speichern?

Dies hängt von der Version und den Datenkomprimierungsoptionen von SQL Server ab:

  • Ab SQL Server 2008 gibt es eine Komprimierungsoption auf Zeilen- oder Seitenebene.
  • Die Komprimierung auf Seitenebene verwendet viele Algorithmen / Techniken zur Komprimierung. In Bezug auf Ihre Frage (Zeiger auf doppelte Daten) verwendet die Seitenkomprimierung (auch) die Präfixkomprimierung und die Wörterbuchkomprimierung :

Prefix Compression [...] Die wiederholten prefix Werte in der Spalte durch einen Verweis auf den entsprechenden Präfix ersetzt sind , [...]

Wörterbuchkomprimierung Nach Abschluss der Präfixkomprimierung wird die Wörterbuchkomprimierung angewendet. Die Wörterbuchkomprimierung sucht überall auf der Seite nach wiederholten Werten und speichert sie im CI-Bereich. Im Gegensatz zur Präfixkomprimierung ist die Wörterbuchkomprimierung nicht auf eine Spalte beschränkt. Die Wörterbuchkomprimierung kann wiederholte Werte ersetzen, die an einer beliebigen Stelle auf einer Seite auftreten. Die folgende Abbildung zeigt dieselbe Seite nach der Wörterbuchkomprimierung.

Für die Präfix- und Wörterbuchkomprimierung (Seitenkomprimierung) verwendet SQL Server Zeiger, um (teilweise oder vollständig) doppelte Werte (keine doppelten Zeilen) in derselben Spalte oder in diff zu speichern . Säulen.

CREATE DATABASE TestComp;
GO

USE TestComp;
GO

CREATE TABLE Person1 (
    PersonID INT IDENTITY PRIMARY KEY,
    FirstName NVARCHAR(100) NOT NULL,
    LastName NVARCHAR(100) NOT NULL
);
GO

DECLARE 
    @f NVARCHAR(100) = REPLICATE('A',100), 
    @l NVARCHAR(100) = REPLICATE('B',100);

INSERT Person1 (FirstName, LastName)
VALUES (@f, @l);
GO 1000

CREATE TABLE Person2 (
    PersonID INT IDENTITY PRIMARY KEY,
    FirstName NVARCHAR(100) NOT NULL,
    LastName NVARCHAR(100) NOT NULL
);
GO

ALTER TABLE Person2
REBUILD
WITH (DATA_COMPRESSION=PAGE);
GO

DECLARE 
    @f NVARCHAR(100) = REPLICATE('A',100), 
    @l NVARCHAR(100) = REPLICATE('B',100);

INSERT Person2 (FirstName, LastName)
VALUES (@f, @l);
GO 1000

SELECT  f.page_count AS PageCount_Person1_Uncompressed
FROM    sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Person1'), 1, DEFAULT, DEFAULT) f
SELECT  f.page_count AS PageCount_Person2_Compressed
FROM    sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Person2'), 1, DEFAULT, DEFAULT) f
GO

Ergebnisse:

PageCount_Person1_Uncompressed
------------------------------
53

PageCount_Person2_Compressed
----------------------------
2
Bogdan Sahlean
quelle