テーブルのデータスペースが生データの4倍のサイズを占めるのはなぜですか?


18

490 M行と55 GBの表スペースがある表があるため、1行あたり約167バイトです。テーブルには3つの列があります:a VARCHAR(100)、a DATETIME2(0)、およびa SMALLINTVARCHARフィールド内のテキストの平均の長さは約21.5なので、生データは行ごとに約32バイトである必要があります。22+ 2はVARCHAR、6はDATETIME2、および2は16ビット整数です。

上記のスペースはデータのみであり、インデックスではないことに注意してください。[プロパティ]で報告される値を使用しています| ストレージ| 全般| データスペース。

もちろん、ある程度のオーバーヘッドが必要ですが、特に大きなテーブルの場合、1行あたり135バイトはかなり多いようです。これはなぜでしょうか?他の誰かが同様の乗数を見ましたか?必要な追加スペースの量に影響する要因は何ですか?

比較のために、2つのINTフィールドと1 M行のテーブルを作成してみました。必要なデータ領域は16.4 MBでした。8バイトの生データと比較して、行あたり17バイトです。INTVARCHAR(100)実際のテーブルと同じテキストが入力された別のテストテーブルは、行ごとに39バイト(44 K行)を使用します。

したがって、実動テーブルにはかなり多くのオーバーヘッドがあります。これは大きいからですか?インデックスサイズはだいたいN * log(N)になると思いますが、実際のデータに必要なスペースが非線形である理由はわかりません。

ポインタを事前に感謝します!

編集:

リストされているすべてのフィールドはNOT NULLです。実際のテーブルには、VARCHARフィールドとDATETIME2フィールドの順にクラスターPKがあります。2つのテストの場合、最初のテストINTは(クラスター化された)PKでした。

重要な場合:テーブルはpingの結果の記録です。フィールドは、URL、ping日付/時刻、およびミリ秒単位の待機時間です。データは常に追加され、更新されることはありませんが、データは定期的に削除され、URLごとに1時間あたり数個のレコードに削減されます。

編集:

非常に興味深い答えがここにいることを示唆している、多くの読み取りと書き込みとインデックスのために、再構築は有益ではないかもしれません。私の場合、消費されるスペースは懸念事項ですが、書き込みパフォーマンスがより重要な場合は、ゆるいインデックスを使用した方が良いかもしれません。

回答:


11

元の質問に関するコメントで議論した後、この場合、失われたスペースはクラスター化されたキーの選択によって引き起こされ、それが大規模な断片化につながったようです。

これらの状況では、sys.dm_db_index_physical_statsを介して常にフラグメンテーションの状態を確認する価値があります。

編集:コメントの更新後

(クラスター化インデックスの再構築前の)平均ページ密度は24%で、元の質問に完全に適合しています。ページはたった1/4だけであったため、合計サイズは生データサイズの4倍でした。


7

ディスク上の構造にはオーバーヘッドがあります。

  • 行ヘッダー
  • nullビットマップ+ポインター
  • 可変長列オフセット
  • 行バージョンポインター(オプション)
  • ...

2 x 4バイトのint列を取ると、

  • 4バイトの行ヘッダー
  • NULLビットマップへの2バイトポインター
  • 2つのint列に対して8バイト
  • 3バイトのNULLビットマップ

すごい17バイト!

元のテーブルのようにオーバーヘッドが多い2番目のテストテーブルでも同じことができます。

  • 可変長列のカウント用に2バイト
  • 可変長列ごとに2バイト

なぜ違いがあるのですか?さらに(これらにはリンクしません)

  • インデックスを再構築してデフラグしましたか?
  • 削除はスペースを再利用しません
  • 中央に挿入するとデータページが分割されます
  • 更新によりフォワードポインターが発生する可能性があります(ギャップを残します)
  • 行オーバーフロー
  • インデックスの再構築またはDBCC CLEANTABLEなしのvarchar列の削除
  • ヒープまたはテーブル(ヒープにはクラスター化インデックスがない=レコードが散在している)
  • RCSI分離レベル(行ごとに余分な14バイト)
  • varcharの末尾のスペース(SET ANSI_PADDINGはデフォルトでON)。LENではなくDATALENGTHを使用してcheclをチェックします
  • sp_spaceusedを実行します @updateusage = 'true'
  • ...

これを参照してください:SQL Server:1つの8 KBページを満たすテーブルを作成する方法は?

SOから:


2x4バイトのint列のサンプルは100%正確ではありません。4バイトの行ヘッダー(2バイトのステータスバイトと固定長のデータサイズ用の2バイト)があります。次に、データ用に2x4バイトが必要です。二つのカラム数とヌルビットマップのための単一のバイト、15バイトの合計レコード長を与えることはなく、17のバイト
マークS.ラスムッセン

@Mark S. Rasmussen:「固定長データサイズの2バイト」はどこで入手できますか?MSDN?そして、ヌルビットマップは常に3バイトです:sqlskills.com/blogs/paul/post/... + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
GBN

うわー、素晴らしいディテール!VARCHAR上記の見積もりではsの長さフィールドを考慮しましたが、列の数は考慮しませんでした。このテーブルにはNULL可能フィールドがありません(それについて言及しているはずです)、それでもバイトを割り当てていますか?
すべての取引のジョン

インデックスの再構築は、必要なスペースのデータ部分に影響しますか?おそらくクラスター化インデックスを再構築すると思います。挿入は、途中で発生しますが、クラスタリングフィールドの順序を入れ替えると停止します。残りのほとんどはこの場合には適用すべきではありませんが、一般的な場合の素晴らしいリファレンスです。リンクをチェックします。良いもの!
すべての取引のジョン

1
@gbn固定長データサイズの2バイトは、言及した4バイトの行ヘッダーの一部です。これは、固定データ長部分の終わり/列カウント/ヌルビットマップの始まりを指すポインターです。NULLビットマップは常に3バイトではありません。列数を含めると、少なくとも3バイトになりますが、それ以上になる場合があります。ビットマップと列数を説明で分割します。また、この場合はNULLビットマップが常に存在するとは限りません。
マークS.ラスムッセン

5

データタイプは時間とともに変化しましたか?可変長列は削除されましたか?インデックスは頻繁に最適化されていますが、再構築されていませんか?多くの行が削除されましたか、または多くの可変長列が大幅に更新されましたか?ここでいくつかの良い議論。


データ型を変更したり、フィールドを削除したりしていないことを97%確信しています。もしそうなら、テーブルがはるかに少ない行を持っていたとき、それは本当に早いでしょう。削除や更新はありません。データが追加されるだけです。
すべての取引のジョン

訂正:そこにあるの削除があり、かなり。テーブルにはかなりの純成長があるので、このスペースはすぐに再利用されると思います。
すべての取引のジョン

大量の削除では、データが再利用される場合とされない場合があります。テーブルのクラスタリングキーとは何ですか?テーブルの中央に挿入しますか、それとも最後に挿入しますか?
mrdenny

クラスター化されたキーは、VARCHARおよびDATETIME2フィールドでこの順序で複合されます。挿入物は最初のフィールドに均等に分配されます。2番目のフィールドでは、新しい値は、常に既存の値よりも大きくなります。
すべての取引のジョン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.