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


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は、データファイル内のデータをページ単位で内部的に編成します。ページは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が新しいページを作成するのはなぜですか?

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

回答:


9

データ行は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を追加すると、8kbのページ制限を超えているため、別のページが強制的に作成されます。


あなたが提供したリンクは、ページ構造をより視覚的にするのに役立ちました、私はそれに似たものを探していましたが、それを見つけることができませんでした、タックス
アルファ

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バイト(バージョン情報あり)です。

を使用DBCC PAGEすると、Record Size 263(for tempdb)、およびRecord Size 277(に設定されているデータベースの場合)を表示して計算を確認しますALLOW_SNAPSHOT_ISOLATION ON

現在、30行あります:

  • バージョン情報なし

    30 * 263は7890バイトになります。次に、使用される7986バイトの96バイトのページヘッダーを追加します。最後に、スロットアレイの60バイト(行ごとに2)を追加して、ページで使用される合計8046バイト、残り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バイト+スロット配列の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)。(Clusteredテーブルを見たとき)VALUEの行に沿って、0x60 MIXED_EXT ALLOCATED 0_PCT_FULLまたは0x64 MIXED_EXT ALLOCATED 100_PCT_FULLHeapテーブルを見たときに、何かが表示されます。これはトランザクションごとに評価されるため、ここで実行されるテストなどの個別の挿入を実行すると、クラスター化テーブルとヒープテーブル間で異なる結果が表示される可能性があります。ただし、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


正確には、ユーザーデータには1ページあたり8060バイトを使用できます。OPのデータはまだその下にあります。
ロジャーウルフ

バージョン情報はありません。それ以外の場合、30行は8310バイトかかります。残りは正しいようです。
ロジャーウルフ

@RogerWolfはい、「バージョン情報」があります。そのため、30行には8310バイトが必要です。OPが使用しているテストプロシージャによってOPが信じられるようになっているため、実際、これらの30行がすべて1ページに収まらないのはこのためです。しかし、そのテストプロシージャは間違っています。ページに収まるのは29行のみです。私はこれを確認しました(SQL Server 2012を使用しても)。
ソロモンラッツキー

RCSI対応ではないデータベースでテストを実行しようとしましたtempdbか?OPから提供された正確な数字を再現することができました。
ロジャーウルフ

@RogerWolf使用しているDBはRCSI対応ではありませんが、に設定されていALLOW_SNAPSHOT_ISOLATIONます。私も試しtempdbたところ、「バージョン情報」が存在しないため、30行が収まることがわかりました。新しい情報を追加するために更新します。
ソロモンラッツキー

3

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

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

PS Kalen Delaneyによる「SQL Server Internals」の本の1つで、データページ構造の詳細な説明をバイト単位で見たと思いますが、ほぼ10年前でしたので、これ以上詳細を覚えていないことを許してください。また、ページ構造はバージョンごとに変わる傾向があります。


1
いいえ。一意キーは、重複するキー行にのみ追加されます。各一意のキー値の最初の行には、余分な4バイトの一意名は含まれません。
ソロモンラッツキー

@srutzky、明らかにあなたは正しい。SQL Serverが可変幅のキーを許可するとは思わなかった。これはいです。効率的、はい、しかしbutい。
ロジャーウルフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.