SQL Server có sử dụng con trỏ thay vì lưu trữ các hàng trùng lặp không?


8

Tôi đang sử dụng sp_spaceusedquy trình được lưu sẵn tích hợp trước và sau khi thực hiện thao tác trong phần mềm của chúng tôi để xem bảng nào có chèn hàng và kích thước của mỗi bảng thay đổi.

Những gì tôi đang thấy là trong số tất cả các bảng có các hàng được viết cho chúng, chỉ có một số ít cho thấy bảng đã tăng ở bên. Những cái khác hiển thị các hàng đã được thêm vào cho thấy không có thay đổi về kích thước từ quy trình được lưu trữ này.

Trường hợp duy nhất không đúng trong giao dịch đầu tiên sau khi thực hiện cắt ngắn trên tất cả các bảng. Vì vậy, đối với tôi, có vẻ như thay vì lưu trữ dữ liệu trùng lặp, SQL Server đang hiển thị các hàng được chèn nhưng phải chỉ lưu trữ các con trỏ tới các hàng giống hệt nhau trước đó.

Bất cứ ai có thể xác nhận điều này xin vui lòng?


Được gắn cờ cho dba.se
gbn

Câu trả lời:


13

Không, SQL Server không phát hiện các hàng trùng lặp

SQL Server đang lấp đầy các trang trống hoặc một phần trống trong các trang được phân bổ.

Vì vậy, nếu tôi có một hàng rất hẹp (2 cột nói), tôi có thể thêm vài trăm hàng nữa trên cùng một trang mà không tăng dung lượng sử dụng.

Bản demo nhanh và bẩn (không có hàng trùng lặp, nhưng bạn có thể chơi với bản này nếu muốn)

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

Cảm ơn đó là một câu trả lời tuyệt vời. Chúc tôi có thể bình chọn nhiều hơn. Có lẽ bạn cũng có thể đề xuất, nếu bạn thật tốt bụng, làm thế nào tôi có thể tìm ra dữ liệu thực tế được sử dụng trong trang. Tất cả những gì tôi có thể thấy là: tên hàng dành riêng cho dữ liệu index_size Các khoản phí chưa sử dụng 6 16 KB 8 KB 8 KB 0 KB Từ đây tôi không thể thấy số lượng trang tôi đang sử dụng cho 6 hàng của mình. Nó nói với tôi rằng tôi đã sử dụng 0KB trong trang này, mặc dù tôi biết đây không phải là trường hợp.

Tôi đã thử DBCC SHOWCONTIG nhưng điều này không hiển thị các cột lớn được lưu trữ trong LOB như tôi hiểu.

Hình tôi sẽ bình luận thay vì tạo một câu hỏi mới ... Làm thế nào để lưu trữ các bảng rộng hơn hoạt động? Điều gì xảy ra nếu ví dụ tôi có một bảng thực sự rộng nhưng hầu hết thời gian 60% hoặc hơn các cột là null? Tôi giả sử hàng này sẽ chiếm cùng một dung lượng để lưu trữ trong trang vì các cột COULD có dữ liệu? Về mặt lưu trữ chỉ (tất nhiên mọi thứ có thể được thực hiện quá theo nghĩa đen) tốt hơn là có nhiều bảng hẹp hơn? Nếu cuối cùng bạn vẫn cần phải kéo các cột "trống" thường xuyên thì có lẽ sẽ hợp lý nếu giữ nó cùng với bảng chính?
bdwakefield

7

SQL Server có sử dụng con trỏ thay vì lưu trữ các hàng trùng lặp không?

Nó phụ thuộc vào các tùy chọn nén dữ liệu và phiên bản của SQL Server:

  • Bắt đầu từ SQL Server 2008, có một tùy chọn nén ở cấp hàng hoặc trang.
  • Nén cấp độ trang sử dụng nhiều thuật toán / kỹ thuật để nén. Về câu hỏi của bạn (con trỏ cho dữ liệu trùng lặp), nén trang sử dụng (cũng) nén tiền tố và nén từ điển :

Nén tiền tố [...] Các giá trị tiền tố lặp lại trong cột được thay thế bằng tham chiếu đến tiền tố tương ứng [...]

Nén từ điển Sau khi nén tiền tố đã hoàn tất, nén từ điển được áp dụng. Nén từ điển tìm kiếm các giá trị lặp lại ở bất cứ đâu trên trang và lưu trữ chúng trong khu vực CI. Không giống như nén tiền tố, nén từ điển không bị hạn chế trong một cột. Nén từ điển có thể thay thế các giá trị lặp lại xảy ra ở bất cứ đâu trên một trang. Hình minh họa sau đây cho thấy cùng một trang sau khi nén từ điển.

Vì vậy, để nén tiền tố và từ điển (nén trang), SQL Server sử dụng các con trỏ để lưu trữ (một phần hoặc toàn bộ) các giá trị trùng lặp (không phải các hàng trùng lặp) trong cùng một cột hoặc trong diff. cột.

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

Các kết quả:

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

PageCount_Person2_Compressed
----------------------------
2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.