SQL Server utilizza i puntatori invece di archiviare righe duplicate?


8

Sto usando la sp_spaceusedprocedura memorizzata integrata prima e dopo aver eseguito un'operazione nel nostro software per vedere quali tabelle hanno inserimenti di riga e come cambia la dimensione di ciascuna tabella.

Quello che vedo è che su tutte le tabelle che vengono scritte righe, solo una manciata mostra che la tabella è aumentata di lato. Gli altri che mostrano le righe aggiunte non mostrano alcun cambiamento nelle dimensioni da questa procedura memorizzata.

L'unico caso non vero è relativo alla prima transazione dopo aver eseguito un troncamento su tutte le tabelle. Quindi a me sembra che invece sulla memorizzazione di dati duplicati SQL Server mostri che le righe sono inserite ma che devono semplicemente archiviare i puntatori su righe identiche precedenti.

Qualcuno può confermare questo per favore?


Contrassegnato per dba.se
gbn il

Risposte:


13

No, SQL Server non rileva righe duplicate

SQL Server sta riempiendo pagine vuote o parzialmente vuote all'interno delle pagine allocate.

Quindi, se ho una riga molto stretta (dicono 2 colonne), posso aggiungere alcune centinaia di righe sulla stessa pagina senza aumentare lo spazio utilizzato.

Demo veloce e sporca (senza righe duplicate, ma puoi giocare con questo se vuoi)

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

Grazie è una risposta così meravigliosa. Vorrei poter votare di più. Forse potresti anche suggerire, se fossi così gentile, come potrei elaborare i dati reali utilizzati all'interno della pagina. Tutto quello che posso vedere è: nome righe dati riservati index_size inutilizzato Spese 6 16 KB 8 KB 8 KB 0 KB Da questo non riesco a vedere la parte della pagina che sto usando per le mie 6 righe. Mi sta dicendo che non ho usato 0 KB in questa pagina, anche se so che non è così.

Ho provato DBCC SHOWCONTIG ma questo non mostra grandi colonne memorizzate nel LOB a quanto ho capito.

Ho pensato di commentare piuttosto che creare una nuova domanda ... Come funziona l'archiviazione di tabelle più grandi? Cosa succede se, ad esempio, ho una tabella veramente ampia ma la maggior parte delle volte il 60% delle colonne è nulla? Presumo che questa riga occuperà la stessa quantità di spazio per l'archiviazione nella pagina poiché quelle colonne POTREBBERO avere dati? In termini di spazio di archiviazione (tutto può essere considerato troppo letterale, ovviamente) è meglio avere più tabelle strette? Se alla fine hai bisogno di inserire spesso le colonne "vuote", probabilmente avrebbe senso tenerlo insieme alla tabella principale?
bdwakefield,

7

SQL Server utilizza i puntatori invece di archiviare righe duplicate?

Dipende dalla versione di SQL Server e dalle opzioni di compressione dei dati:

  • A partire da SQL Server 2008, esiste un'opzione di compressione a livello di riga o di pagina.
  • La compressione a livello di pagina utilizza molti algoritmi / tecniche per la compressione. Per quanto riguarda la tua domanda (puntatori per i dati duplicati), la compressione della pagina utilizza (anche) la compressione del prefisso e la compressione del dizionario :

Compressione prefisso [...] I valori di prefisso ripetuti nella colonna sono sostituiti da un riferimento al prefisso corrispondente [...]

Compressione del dizionario Al termine della compressione del prefisso, viene applicata la compressione del dizionario. La compressione del dizionario cerca valori ripetuti in qualsiasi punto della pagina e li memorizza nell'area CI. A differenza della compressione dei prefissi, la compressione del dizionario non è limitata a una colonna. La compressione del dizionario può sostituire i valori ripetuti che si verificano ovunque in una pagina. La seguente illustrazione mostra la stessa pagina dopo la compressione del dizionario.

Pertanto, per la compressione del prefisso e del dizionario (compressione della pagina), SQL Server utilizza i puntatori per archiviare (parzialmente o completamente) valori duplicati (non righe duplicate) all'interno della stessa colonna o all'interno di diff. colonne.

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

risultati:

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

PageCount_Person2_Compressed
----------------------------
2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.