web-dev-qa-db-ja.com

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

私は490 M行と55 GBのテーブルスペースを持つテーブルを持っているので、行あたり約167バイトです。テーブルには、VARCHAR(100)DATETIME2(0)、およびSMALLINTの3つの列があります。 VARCHARフィールドのテキストの平均長は約21.5であるため、生データは1行あたり約32バイトである必要があります。VARCHARは22 + 2、DATETIME2は6、2 16ビット整数の場合。

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

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

比較のために、2つのINTフィールドと100万行のテーブルを作成してみました。必要なデータスペースは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時間あたり数レコードだけに削減されます。

編集:

非常に興味深い答え here は、読み取りと書き込みが多いインデックスの場合、再構築は有益ではない可能性があることを示唆しています。私の場合、消費される領域が問題になりますが、書き込みパフォーマンスがより重要な場合は、たるんだインデックスを使用する方がよいでしょう。

18

元の質問のコメントでの議論の後、この場合、失われたスペースはクラスター化されたキーの選択が原因であると思われます。

これらの状況では、sys.dm_db_index_physical_statsを使用して断片化の状態を常に確認する価値があります。

編集:コメントの更新後

(クラスター化インデックスの再構築前の)平均ページ密度は24%で、元の質問と完全に一致しています。ページは1/4の空き容量しかなかったため、合計サイズはrawデータサイズの4倍でした。

11

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

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

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

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

すごい17バイト!

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

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

なぜ違いますか?さらに(私はこれらにリンクしません)

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

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

SOから:

7
gbn

時間の経過とともにデータ型が変更されましたか?可変長列は削除されましたか?インデックスは頻繁にデフラグされていますが、再構築されていませんか?多数の行が削除されたか、多数の可変長列が大幅に更新されましたか?良い議論 ここ

5
Aaron Bertrand