512 바이트는 SQL Server의 8KB 데이터 페이지에서 사용되지 않습니다


13

다음 테이블을 만들었습니다.

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

그런 다음 클러스터형 인덱스를 만들었습니다.

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

다음으로 각 크기가 256 바이트 인 30 행으로 채 웁니다 (테이블 선언에 따라).

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;

이제 "훈련 키트 (시험 70-461) : Microsoft SQL Server 2012 쿼리 (Itzik Ben-Gan)"책에서 읽은 정보를 바탕으로합니다.

SQL Server는 내부적으로 데이터 파일의 데이터를 페이지 단위로 구성합니다. 페이지는 8KB 단위이며 단일 객체에 속합니다. 예를 들어, 테이블이나 인덱스. 페이지는 읽고 쓰는 가장 작은 단위입니다. 페이지는 추가 범위로 ​​구성됩니다. 익스텐트는 8 개의 연속 페이지로 구성됩니다. 익스텐트의 페이지는 단일 객체 또는 여러 객체에 속할 수 있습니다. 페이지가 여러 객체에 속하는 경우 범위를 혼합 범위라고합니다. 페이지가 단일 객체에 속하는 경우 범위를 균일 한 범위라고합니다. SQL Server는 개체의 처음 8 페이지를 혼합 된 범위로 저장합니다. 개체가 8 페이지를 초과하면 SQL Server는이 개체에 대해 추가로 균일 한 범위를 할당합니다. 이 구성을 사용하면 작은 개체는 공간을 덜 차지하고 큰 개체는 조각화가 줄어 듭니다.

그래서 여기에 7680 바이트로 채워진 첫 번째 혼합 범위 8KB 페이지가 있습니다 (256 바이트 크기 행을 30 배 삽입하므로 30 * 256 = 7680). 크기 검사 proc을 실행 한 크기를 확인하려면 다음 결과를 반환합니다.

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

따라서 16KB는 테이블 용으로 예약되어 있으며 첫 8KB 페이지는 Root IAM 페이지 용이고, 두 번째는 8KB의 리프 데이터 스토리지 페이지 용으로 ~ 7.5KB를 점유하고 256 바이트의 새 행을 삽입 할 때 다음과 같습니다.

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

256 바이트의 공간 (7680 b + 256 = 7936, 여전히 8KB보다 작음)이지만 동일한 페이지에 저장되지 않고 새 데이터 페이지가 작성되지만 새 행이 동일한 이전 페이지에 맞을 수 있습니다 , SQL Server가 공간을 절약하고 검색 시간을 기존 페이지에 삽입 할 때 새 페이지를 만드는 이유는 무엇입니까?

참고 : 힙 인덱스에서도 마찬가지입니다.

답변:


9

데이터 행이 256 바이트가 아닙니다. 각각은 263 바이트와 비슷합니다. 순수 고정 길이 데이터 형식의 데이터 행에는 SQL Server의 데이터 행 구조로 인해 추가 오버 헤드가 있습니다. 이 사이트를보고 데이터 행을 구성하는 방법에 대해 읽으십시오. http://aboutsqlserver.com/2013/10/15/sql-server-storage-engine-data-pages-and-data-rows/

예를 들어 256 바이트의 데이터 행이 있고 상태 비트의 경우 2 바이트, 열 수의 경우 2 바이트, 데이터 길이의 경우 2 바이트, 널 비트 맵의 ​​경우 1 또는 다른 바이트를 추가하십시오. 263 * 30 = 7,890 바이트입니다. 다른 263을 추가하면 8KB 페이지 제한을 초과하여 다른 페이지가 작성됩니다.


당신이 제공 한 링크는 페이지 구조를 더 잘 시각화하는 데 도움이되었지만 비슷한 것을 검색했지만 찾을 수 없었습니다. Thax
Alphas Supremum

11

SQL Server가 8k (8192 바이트) 데이터 페이지를 사용하여 1 개 이상의 행을 저장하는 것이 사실이지만 각 데이터 페이지에는 약간의 오버 헤드 (96 바이트)가 있으며 각 행에는 약간의 오버 헤드 (최소 9 바이트)가 있습니다. 8192 바이트는 순수한 데이터가 아닙니다.

이것이 어떻게 작동하는지에 대한 자세한 검사는 다음 DBA.SE 질문에 대한 나의 대답을 참조하십시오.

sys.allocation_units의 테이블 크기와 일치하지 않는 DATALENGTH의 합

링크 된 답변의 정보를 사용하여 실제 행 크기를보다 명확하게 알 수 있습니다.

  1. 행 헤더 = 4 바이트
  2. 열 수 = 2 바이트
  3. NULL 비트 맵 = 1 바이트
  4. 버전 정보 ** = 14 바이트 (선택 사항, 각주 참조)
  5. 총 행 오버 헤드 (슬롯 배열 제외) = 최소 7 바이트 또는 버전 정보가있는 경우 21 바이트
  6. 총 실제 행 크기 = 최소 263 (256 데이터 + 7 오버 헤드) 또는 버전 정보가있는 경우 277 바이트 (256 데이터 + 21 오버 헤드)
  7. 슬롯 배열에 추가하면 행당 차지하는 총 공간은 실제로 265 바이트 (버전 정보 없음) 또는 279 바이트 (버전 정보 있음)입니다.

를 사용하면 (for ) 및 (로 설정된 데이터베이스 ) DBCC PAGE를 표시 하여 계산을 확인 합니다.Record Size 263tempdbRecord Size 277ALLOW_SNAPSHOT_ISOLATION ON

이제 30 행으로 다음과 같습니다.

  • 버전 정보없이

    30 * 263은 7890 바이트를 제공합니다. 그런 다음 96 바이트의 페이지 헤더를 7986 바이트에 추가하십시오. 마지막으로, 페이지에 사용 된 총 8046 바이트에 대해 슬롯 배열의 60 바이트 (행당 2 개)를 추가하고 나머지 146 개를 추가하십시오. 를 사용하면 DBCC PAGE다음을 표시 하여 계산을 확인합니다.

    • m_slotCnt 30 (즉, 행 수)
    • m_freeCnt 146 (즉, 페이지에 남은 바이트 수)
    • m_freeData 7986 (즉, 데이터 + 페이지 헤더-7890 + 96-슬롯 배열은 "사용 된"바이트 계산에 포함되지 않습니다)
  • 버전 정보

    30 * 277 바이트, 총 8310 바이트 그러나 8310은 8192 이상이며 96 바이트 페이지 헤더 또는 행 당 2 바이트 슬롯 배열 (30 * 2 = 60 바이트)을 고려하지 않았으므로 행에 사용 가능한 바이트는 8036 개뿐입니다.

    그러나 29 행은 어떻습니까? 그것은 8033 바이트의 데이터 (29 * 277) + 페이지 헤더의 경우 96 바이트 + 8187 바이트와 같은 슬롯 배열 (29 * 2)의 경우 58 바이트를 제공합니다. 그리고 5 바이트가 남은 페이지가 남습니다 (8192-8187; 물론 사용할 수 없음). 를 사용하면 DBCC PAGE다음을 표시 하여 계산을 확인합니다.

    • m_slotCnt 29 (즉, 행 수)
    • m_freeCnt 5 (즉, 페이지에 남은 바이트 수)
    • m_freeData 8129 (즉, 데이터 + 페이지 헤더-8033 + 96-슬롯 배열은 "사용 된"바이트 계산에 포함되지 않습니다)

힙 관련

힙은 데이터 페이지를 약간 다르게 채 웁니다. 페이지에 남은 공간의 대략적인 추정치를 유지합니다. DBCC 출력을 볼 때 다음 행을 찾으십시오 PAGE HEADER: Allocation Status PFS (1:1).. (클러스터 된 테이블을 볼 때) 또는 VALUE라인을 따라 표시되는 것을 볼 수 있습니다.0x60 MIXED_EXT ALLOCATED 0_PCT_FULL0x64 MIXED_EXT ALLOCATED 100_PCT_FULL 때 힙 테이블을 됩니다. 이는 트랜잭션별로 평가되므로 여기에서 수행되는 테스트와 같은 개별 삽입을 수행하면 클러스터 테이블과 힙 테이블간에 서로 다른 결과가 표시 될 수 있습니다. 그러나 30 개 행 모두에 대해 단일 DML 작업을 수행하면 힙이 예상대로 채워집니다.

그러나 테이블의 두 버전이 146 바이트 만 남은 30 개의 행에 맞기 때문에 힙에 관한 이러한 세부 사항은이 특정 테스트에 직접 영향을 미치지 않습니다. 클러스터링 또는 힙에 관계없이 다른 행을위한 공간이 충분하지 않습니다.

이 테스트는 다소 단순하다는 점 을 명심하십시오 . 행의 실제 크기 계산은 SPARSE, 데이터 압축, LOB 데이터 등과 같은 다양한 요소에 따라 매우 복잡 할 수 있습니다 .


데이터 페이지의 세부 사항을 보려면 다음 쿼리를 사용하십시오.

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;

** 데이터베이스가 ALLOW_SNAPSHOT_ISOLATION ON또는 로 설정된 경우 14 바이트 "버전 정보"값이 나타납니다 READ_COMMITTED_SNAPSHOT ON.


사용자 데이터에 대해 페이지 당 8060 바이트를 사용할 수 있습니다. OP의 데이터는 여전히 그 아래에 있습니다.
Roger Wolf

버전 정보가 없으면 30 행에 8310 바이트가 필요합니다. 나머지는 올바른 것 같습니다.
Roger Wolf

@RogerWolf 예, "버전 정보"가 있습니다. 따라서 30 행에는 8310 바이트가 필요합니다. 그렇기 때문에 OP가 사용중인 테스트 프로세스에 따라 OP가 믿기 때문에 30 행이 실제로 1 페이지에 맞지 않는 이유입니다. 그러나 그 시험 절차는 잘못되었습니다. 페이지에는 29 행만 맞습니다. 나는 이것을 확인했다 (SQL Server 2012조차도 사용).
Solomon Rutzky

RCSI 가능 /이 아닌 데이터베이스에서 테스트를 실행하려고 tempdb했습니까? OP에서 제공 한 정확한 숫자를 재현 할 수있었습니다.
Roger Wolf

@RogerWolf 사용중인 DB가 RCSI를 지원하지 않지만로 설정되어 ALLOW_SNAPSHOT_ISOLATION있습니다. 또한 방금 시도한 tempdb결과 "버전 정보"가 없으므로 30 행이 적합합니다. 새 정보를 추가하도록 업데이트하겠습니다.
Solomon Rutzky

3

데이터 페이지의 실제 구조는 매우 복잡합니다. 일반적으로 사용자 데이터에 페이지 당 8060 바이트를 사용할 수 있다고 명시되어 있지만,이 동작을 초래하는 일부 오버 헤드가 계산되지 않습니다.

그러나 SQL Server가 실제로 31 행이 페이지에 맞지 않을 것이라는 힌트를 제공한다는 것을 알았을 것입니다. 다음 행을 같은 페이지에 avg_page_space_used_in_percent맞추 려면 값이 100 %-(100 / 31) = 96.774194 미만이어야하며 귀하의 경우에는 그보다 높아야합니다.

추신 Kalen Delaney의 "SQL Server Internals"책 중 하나에서 데이터 페이지 구조에 대한 바이트 설명에 대한 자세한 내용을 보았지만 거의 10 년 전에 더 자세한 내용을 기억하지 못해서 실례합니다. 게다가, 페이지 구조는 버전마다 바뀌는 경향이 있습니다.


1
아니요. 단일화자는 키 행을 복제하기 위해서만 추가됩니다. 각 고유 키 값의 첫 번째 행에는 추가 4 바이트 고유자가 포함되지 않습니다.
Solomon Rutzky

@ srutzky, 분명히 당신이 맞습니다. SQL Server가 가변 너비 키를 허용한다고 생각하지 않았습니다. 이것은 추악합니다. 효율적입니다. 그러나 추악합니다.
Roger Wolf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.