512 Byte không được sử dụng từ trang dữ liệu 8 KByte của SQL Server


13

Tôi đã tạo bảng sau:

CREATE TABLE dbo.TestStructure
(
    id INT NOT NULL,
    filler1 CHAR(36) NOT NULL,
    filler2 CHAR(216) NOT NULL
);

và sau đó tạo một chỉ mục cụm:

CREATE CLUSTERED INDEX idx_cl_id 
ON dbo.TestStructure(id);

Tiếp theo tôi đã điền nó với 30 hàng mỗi kích thước là 256 byte (dựa trên khai báo bảng):

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;

Bây giờ dựa trên thông tin tôi đã đọc trong sách "Bộ công cụ đào tạo (Bài kiểm tra 70-461): Truy vấn Microsoft SQL Server 2012 (Itzik Ben-Gan)":

SQL Server tổ chức nội bộ dữ liệu trong một tệp dữ liệu trong các trang. Một trang là một đơn vị 8 KB và thuộc về một đối tượng; ví dụ, để một bảng hoặc một chỉ mục. Một trang là đơn vị nhỏ nhất của đọc và viết. Các trang được tiếp tục tổ chức thành phạm vi. Một phạm vi bao gồm tám trang liên tiếp. Các trang từ một phạm vi có thể thuộc về một đối tượng hoặc nhiều đối tượng. Nếu các trang thuộc về nhiều đối tượng, thì phạm vi được gọi là phạm vi hỗn hợp; nếu các trang thuộc về một đối tượng, thì phạm vi được gọi là phạm vi thống nhất. SQL Server lưu trữ tám trang đầu tiên của một đối tượng trong phạm vi hỗn hợp. Khi một đối tượng vượt quá tám trang, SQL Server sẽ phân bổ các phạm vi thống nhất bổ sung cho đối tượng này. Với tổ chức này, các vật thể nhỏ lãng phí ít không gian hơn và các vật thể lớn ít bị phân mảnh hơn.

Vì vậy, ở đây tôi có trang 8KB phạm vi hỗn hợp đầu tiên, được điền với 7680 byte (tôi đã chèn 30 lần hàng có kích thước 256 byte, vì vậy 30 * 256 = 7680), để kiểm tra kích thước tôi đã chạy kiểm tra kích thước Proc - nó trả về kết quả sau

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

Vì vậy, 16 KB được dành riêng cho bảng, trang 8 KB đầu tiên dành cho trang Root IAM, trang thứ hai dành cho trang lưu trữ dữ liệu lá có dung lượng 8KB với mức chiếm ~ 7,5 KB, bây giờ khi tôi chèn một hàng mới với 256 Byte:

INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');

nó không được lưu trong cùng một trang mặc dù nó có không gian 256 byte (7680 b + 256 = 7936 vẫn nhỏ hơn 8KB), một trang dữ liệu mới được tạo, nhưng hàng mới đó có thể vừa trên cùng một trang cũ , tại sao SQL Server tạo một trang mới khi nó có thể tiết kiệm không gian và thời gian tìm kiếm mua chèn nó vào trang hiện có?

Lưu ý: điều tương tự đang xảy ra trong chỉ số heap.

Câu trả lời:


9

Hàng dữ liệu của bạn không phải là 256 byte. Mỗi cái giống như 263 byte. Một hàng dữ liệu gồm các loại dữ liệu có độ dài cố định hoàn toàn có thêm chi phí do cấu trúc của một hàng dữ liệu trong SQL Server. Hãy xem trang web này và đọc về cách một hàng dữ liệu được tạo thành. http://aboutsqlserver.com/2013/10/15/sql-server-st Storage-engine-data-pages-and-data-rows/

Vì vậy, trong ví dụ của bạn, bạn có một hàng dữ liệu có 256byte, thêm 2 byte cho các bit trạng thái, 2 byte cho số cột, 2 byte cho chiều dài dữ liệu và 1 bit khác cho bitmap null. Đó là 263 * 30 = 7.890byte. Thêm một 263 khác và bạn vượt quá giới hạn trang 8kb, điều này sẽ buộc một trang khác được tạo.


Liên kết mà bạn cung cấp đã giúp tôi hình dung rõ hơn về cấu trúc trang, tôi đã tìm kiếm thứ gì đó tương tự nhưng không thể tìm thấy nó, Thax
Alphas Supremum

11

Mặc dù đúng là SQL Server sử dụng các trang dữ liệu 8k (8192 byte) để lưu trữ 1 hàng trở lên, mỗi trang dữ liệu có một số chi phí (96 byte) và mỗi hàng có một số chi phí (ít nhất là 9 byte). 8192 byte không hoàn toàn là dữ liệu.

Để kiểm tra chi tiết hơn về cách thức hoạt động của nó, vui lòng xem câu trả lời của tôi cho câu hỏi DBA.SE sau:

SUM của DATALENGTH không khớp với kích thước bảng từ sys.allocation_units

Sử dụng thông tin trong câu trả lời được liên kết đó, chúng ta có thể có được một bức tranh rõ ràng hơn về kích thước hàng thực tế:

  1. Tiêu đề hàng = 4 byte
  2. Số cột = 2 byte
  3. Bitmap NULL = 1 byte
  4. Thông tin phiên bản ** = 14 byte (tùy chọn, xem chú thích)
  5. Tổng chi phí trên mỗi hàng (không bao gồm Slot Array) = tối thiểu 7 byte hoặc 21 byte nếu có thông tin phiên bản
  6. Tổng kích thước hàng thực tế = tối thiểu 263 (256 dữ liệu + 7 chi phí) hoặc 277 byte (256 dữ liệu + 21 chi phí) nếu có thông tin phiên bản
  7. Thêm vào Slot Array, tổng dung lượng được lấy trên mỗi hàng thực tế là 265 byte (không có thông tin phiên bản) hoặc 279 byte (có thông tin phiên bản).

Việc sử dụng DBCC PAGExác nhận tính toán của tôi bằng cách hiển thị: Record Size 263(for tempdb) và Record Size 277(cho cơ sở dữ liệu được đặt thành ALLOW_SNAPSHOT_ISOLATION ON).

Bây giờ, với 30 hàng, đó là:

  • KHÔNG CÓ thông tin phiên bản

    30 * 263 sẽ cung cấp cho chúng tôi 7890 byte. Sau đó thêm vào 96 byte tiêu đề trang cho 7986 byte được sử dụng. Cuối cùng, thêm 60 byte (2 mỗi hàng) của mảng vị trí cho tổng số 8046 byte được sử dụng trên trang và 146 còn lại. Sử dụng DBCC PAGExác nhận tính toán của tôi bằng cách hiển thị:

    • m_slotCnt 30 (tức là số lượng hàng)
    • m_freeCnt 146 (tức là số byte còn lại trên trang)
    • m_freeData 7986 (tức là dữ liệu + tiêu đề trang - 7890 + 96 - mảng vị trí không được tính vào tính toán byte "đã sử dụng")
  • VỚI thông tin phiên bản

    30 * 277 byte cho tổng số 8310 byte. Nhưng 8310 là hơn 8192 và thậm chí không tính đến tiêu đề trang 96 byte cũng như mảng khe 2 byte trên mỗi hàng (30 * 2 = 60 byte) sẽ chỉ cung cấp cho chúng tôi 8036 byte có thể sử dụng cho các hàng.

    NHƯNG, còn 29 hàng thì sao? Điều đó sẽ cung cấp cho chúng tôi 8033 byte dữ liệu (29 * 277) + 96 byte cho tiêu đề trang + 58 byte cho mảng vị trí (29 * 2) bằng 8187 byte. Và điều đó sẽ rời khỏi trang với 5 byte còn lại (tất nhiên là 8192 - 8187; không sử dụng được). Sử dụng DBCC PAGExác nhận tính toán của tôi bằng cách hiển thị:

    • m_slotCnt 29 (tức là số lượng hàng)
    • m_freeCnt 5 (tức là số byte còn lại trên trang)
    • m_freeData 8129 (tức là dữ liệu + tiêu đề trang - 8033 + 96 - mảng vị trí không được tính vào tính toán byte "đã sử dụng")

Về đống

Heaps điền vào các trang dữ liệu hơi khác nhau. Họ duy trì một ước tính rất sơ bộ về dung lượng còn lại trên trang. Khi nhìn vào đầu ra DBCC, hãy nhìn vào hàng cho : PAGE HEADER: Allocation Status PFS (1:1). Bạn sẽ thấy VALUEhiển thị một cái gì đó dọc theo dòng 0x60 MIXED_EXT ALLOCATED 0_PCT_FULL(khi tôi nhìn vào bảng Clustered) hoặc 0x64 MIXED_EXT ALLOCATED 100_PCT_FULLkhi nhìn vào bảng Heap. Điều này được đánh giá trên mỗi Giao dịch, do đó, việc thực hiện các chèn riêng lẻ như thử nghiệm đang được thực hiện ở đây có thể hiển thị các kết quả khác nhau giữa các bảng Clustered và Heap. Tuy nhiên, thực hiện một thao tác DML cho tất cả 30 hàng sẽ lấp đầy đống như mong đợi.

Tuy nhiên, không có chi tiết nào trong số này liên quan đến Heaps ảnh hưởng trực tiếp đến thử nghiệm cụ thể này vì cả hai phiên bản của bảng phù hợp với 30 hàng chỉ còn lại 146 byte. Đó là không đủ không gian cho một hàng khác, bất kể Clustered hay Heap.

Xin lưu ý rằng bài kiểm tra này khá đơn giản. Việc tính toán kích thước thực tế của một hàng có thể trở nên rất phức tạp tùy thuộc vào các yếu tố khác nhau, chẳng hạn như : SPARSE, Nén dữ liệu, dữ liệu LOB, v.v.


Để xem chi tiết của trang dữ liệu, sử dụng truy vấn sau:

DECLARE @PageID INT,
        @FileID INT,
        @DatabaseID SMALLINT = DB_ID();

SELECT  @FileID = alloc.[allocated_page_file_id],
        @PageID = alloc.[allocated_page_page_id]
FROM    sys.dm_db_database_page_allocations(@DatabaseID,
                            OBJECT_ID(N'dbo.TestStructure'), 1, NULL, 'DETAILED') alloc
WHERE   alloc.[previous_page_page_id] IS NULL -- first data page
AND     alloc.[page_type] = 1; -- DATA_PAGE

DBCC PAGE(@DatabaseID, @FileID, @PageID, 3) WITH TABLERESULTS;

** Giá trị "thông tin phiên bản" 14 byte sẽ xuất hiện nếu cơ sở dữ liệu của bạn được đặt thành ALLOW_SNAPSHOT_ISOLATION ONhoặc READ_COMMITTED_SNAPSHOT ON.


Chính xác là 8060 byte cho mỗi trang có sẵn cho dữ liệu người dùng. Dữ liệu của OP vẫn còn dưới mức đó.
Roger Wolf

Thông tin phiên bản không có ở đó, nếu không 30 hàng sẽ mất 8310 byte. Phần còn lại dường như là chính xác.
Roger Wolf

@RogerWolf Có, "thông tin phiên bản" là có. Và như vậy, có 30 hàng yêu cầu 8310 byte. Trên thực tế, đó là lý do tại sao 30 hàng đó không phù hợp với 1 trang vì OP đang được tin tưởng bởi bất kỳ thử nghiệm nào mà OP đang sử dụng. Nhưng thử nghiệm đó là sai. Chỉ có 29 hàng vừa vặn trên trang. Tôi đã xác nhận điều này (sử dụng SQL Server 2012 ngay cả).
Solomon Rutzky

bạn đã thử chạy thử nghiệm trên cơ sở dữ liệu không hỗ trợ RCSI chưa tempdb? Tôi đã có thể sao chép các con số chính xác do OP cung cấp.
Roger Wolf

@RogerWolf DB tôi đang sử dụng không hỗ trợ RCSI, nhưng nó được đặt thành ALLOW_SNAPSHOT_ISOLATION. Tôi cũng vừa thử tempdbvà thấy rằng "thông tin phiên bản" không có ở đó, do đó 30 hàng phù hợp. Tôi sẽ cập nhật để thêm thông tin mới.
Solomon Rutzky

3

Cấu trúc thực tế của trang dữ liệu khá phức tạp. Mặc dù thông thường nói rằng 8060 byte mỗi trang có sẵn cho dữ liệu người dùng, có một số chi phí bổ sung không được tính ở bất kỳ đâu dẫn đến hành vi này.

Tuy nhiên, bạn có thể nhận thấy rằng SQL Server thực sự cung cấp cho bạn một gợi ý rằng hàng thứ 31 sẽ không vừa với trang. Để hàng tiếp theo vừa trên cùng một trang, avg_page_space_used_in_percentgiá trị phải dưới 100% - (100/31) = 96.774194 và trong trường hợp của bạn, nó vượt quá mức đó.

PS Tôi tin rằng tôi đã thấy một lời giải thích chi tiết về cấu trúc trang dữ liệu trong một trong những cuốn sách "SQL Server Internals" của Kalen Delaney, nhưng đã gần 10 năm rồi, xin vui lòng xin lỗi vì tôi không nhớ thêm chi tiết nào. Bên cạnh đó, cấu trúc trang có xu hướng thay đổi từ phiên bản này sang phiên bản khác.


1
Không. Trình duy nhất chỉ được thêm vào các hàng khóa trùng lặp. Hàng đầu tiên của mỗi giá trị khóa duy nhất không bao gồm bộ duy nhất 4 byte.
Solomon Rutzky

@srutzky, hình như bạn nói đúng. Không bao giờ nghĩ rằng SQL Server sẽ cho phép các khóa chiều rộng thay đổi. Điều này thật xấu xí. Hiệu quả, có, nhưng xấu.
Roger Wolf
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.