web-dev-qa-db-ja.com

SQL Serverの8 KBデータページから512バイトが使用されていません

次のテーブルを作成しました:

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);

次に、各行に30行を追加し、各サイズは256バイトです(テーブル宣言に基づく):

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は、ページ内のデータファイル内のデータを内部的に整理します。ページは8 KBの単位であり、単一のオブジェクトに属しています。たとえば、テーブルやインデックスに。ページは、読み取りと書き込みの最小単位です。ページはさらにエクステントに編成されています。エクステントは8つの連続したページで構成されます。エクステントのページは、単一のオブジェクトまたは複数のオブジェクトに属することができます。ページが複数のオブジェクトに属している場合、エクステントは混合エクステントと呼ばれます。ページが単一のオブジェクトに属している場合、エクステントは均一エクステントと呼ばれます。 SQL Serverは、オブジェクトの最初の8ページを混合エクステントに格納します。オブジェクトが8ページを超えると、SQL Serverはこのオブジェクトに追加の均一エクステントを割り当てます。この構成により、小さなオブジェクトは無駄なスペースが減り、大きなオブジェクトは断片化されにくくなります。

したがって、ここでは、最初の混合エクステント8KBページに7680バイト(256バイトサイズの行を30回挿入したため、30 * 256 = 7680を挿入)があり、サイズチェックプロシージャを実行してサイズを確認しています-次の結果を返します

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

したがって、16 KBはテーブル用に予約されています。最初の8 KBページはルートIAMページ用、2番目は8 KBのリーフデータストレージページ用で、占有率は7.5 KB以下で、256バイトの新しい行を挿入すると次のようになります。

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

256バイト(7680 b + 256 = 7936、8KBよりも小さい)のスペースがあっても、同じページに格納されず、新しいデータページが作成されますが、その新しい行は同じ古いページに収まる可能性があります。 、SQL Serverが既存のページに挿入してスペースと検索時間を節約できるのに、なぜ新しいページを作成するのですか?

注:ヒープインデックスでも同じことが起こります。

13
Alphas Supremum

データ行が256バイトではありません。それぞれが263バイトのようです。純粋に固定長のデータ型のデータ行には、SQL Serverのデータ行の構造により、追加のオーバーヘッドがあります。このサイトを見て、データ行がどのように構成されるかについて読んでください。 http://aboutsqlserver.com/2013/10/15/sql-server-storage-engine-data-pages-and-data-rows/

したがって、あなたの例では、256バイトのデータ行があり、ステータスビットに2バイト、列数に2バイト、データ長に2バイト、そしてnullビットマップにさらに1バイト追加します。つまり、263 * 30 = 7,890バイトです。さらに263を追加すると、8 KBのページ制限を超えてしまい、別のページが作成されることになります。

9
dfundako

SQL Serverが8k(8192バイト)のデータページを使用して1つ以上の行を格納することは事実ですが、各データページにはオーバーヘッド(96バイト)があり、各行にはオーバーヘッド(少なくとも9バイト)があります。 8192バイトは純粋なデータではありません。

これがどのように機能するかの詳細な調査については、次のDBA.SEの質問に対する私の回答を参照してください。

DATALENGTHの合計がsys.allocation_unitsのテーブルサイズと一致しない

そのリンクされた回答の情報を使用すると、実際の行サイズをより明確に把握できます。

  1. 行ヘッダー= 4バイト
  2. 列数= 2バイト
  3. NULLビットマップ= 1バイト
  4. バージョン情報** = 14バイト(オプション、脚注を参照)
  5. 行あたりの合計オーバーヘッド(スロットアレイを除く)=最小7バイト、またはバージョン情報が存在する場合は21バイト
  6. 実際の合計行サイズ=最小263(256データ+ 7オーバーヘッド)、またはバージョン情報が存在する場合は277バイト(256データ+ 21オーバーヘッド)
  7. スロット配列に追加すると、1行あたりの合計スペースは実際には265バイト(バージョン情報なし)または279バイト(バージョン情報あり)のいずれかになります。

_DBCC PAGE_を使用すると、_Record Size 263_(tempdbの場合)、および_Record Size 277_(_ALLOW_SNAPSHOT_ISOLATION ON_に設定されているデータベースの場合)を表示して、計算を確認します。

これで、30行になります。

  • WITHOUTバージョン情報

    30 * 263は7890バイトになります。次に、使用した7986バイトのページヘッダーに96バイトを追加します。最後に、スロットアレイの60バイト(行ごとに2つ)を追加して、ページで使用される合計8046バイト、残りの146バイトを追加します。 _DBCC PAGE_を使用すると、次のように表示され、計算が確認されます。

    • _m_slotCnt 30_(つまり、行数)
    • _m_freeCnt 146_(つまり、ページに残っているバイト数)
    • _m_freeData 7986_(つまり、データ+ページヘッダー-7890 + 96-スロット配列は、「使用済み」バイトの計算に含まれません)
  • WITHバージョン情報

    30 * 277バイト、合計8310バイト。しかし、8310は8192を超えており、96バイトのページヘッダーも2バイトの行スロット配列(30 * 2 = 60バイト)も考慮されていなかったため、行に使用できるのは8036バイトだけでした。

    しかし、29行はどうですか?これにより、データは8033バイト(29 * 277)+ページヘッダー用96バイト+スロット配列用58バイト(29 * 2)となり、8187バイトになります。そして、それはページを残り5バイトのままにします(もちろん8192-8187;もちろん使用不可)。 _DBCC PAGE_を使用すると、次のように表示され、計算が確認されます。

    • _m_slotCnt 29_(つまり、行数)
    • _m_freeCnt 5_(つまり、ページに残っているバイト数)
    • _m_freeData 8129_(つまり、データ+ページヘッダー-8033 + 96-スロット配列は、「使用済み」バイトの計算に考慮されません)

ヒープについて

ヒープは、データページをわずかに異なる方法で埋めます。彼らは、ページに残っているスペースの量の非常に大まかな見積もりを維持します。 DBCC出力を確認するときは、PAGE HEADER: Allocation Status PFS (1:1)の行を確認してください。 _0x60 MIXED_EXT ALLOCATED 0_PCT_FULL_(クラスターテーブルを参照した場合)または_0x64 MIXED_EXT ALLOCATED 100_PCT_FULL_(ヒープテーブルを参照した場合)の行に沿ってVALUEが何かを表示しているのがわかります。これはトランザクションごとに評価されるため、ここで実行されているテストなどの個別の挿入を実行すると、クラスター化テーブルとヒープテーブルで異なる結果が表示される可能性があります。ただし、30行すべてに対して単一のDML操作を実行すると、期待どおりにヒープがいっぱいになります。

ただし、ヒープに関するこれらの詳細は、この特定のテストに直接影響しません。これは、テーブルの両方のバージョンが30行に収まり、残りのバイト数は146バイトだけであるためです。クラスター化またはヒープに関係なく、別の行に十分なスペースではありません。

このテストはかなり単純であることを覚えておいてください。行の実際のサイズの計算は、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;
_

** 14バイトの「バージョン情報」値は、データベースが_ALLOW_SNAPSHOT_ISOLATION ON_または_READ_COMMITTED_SNAPSHOT ON_に設定されている場合に存在します。

11
Solomon Rutzky

データページの実際の構造は非常に複雑です。一般的に、ページごとに8060バイトがユーザーデータに使用できると述べられていますが、この動作につながるオーバーヘッドはどこにもカウントされません。

ただし、SQL Serverが実際に31行目がページに収まらないというヒントを提供していることに気づいたかもしれません。次の行が同じページに収まるように、avg_page_space_used_in_percent値は100%-(100/31)= 96.774194未満である必要があり、あなたの場合はそれをはるかに上回ります。

追伸私は、Kalen Delaneyによる "SQL Server Internals"の本の1つでデータページ構造の詳細なバイト説明まで見たと思いますが、それは約10年前だったので、これ以上詳細を覚えていません。さらに、ページ構造はバージョンごとに変わる傾向があります。

3
Roger Wolf