web-dev-qa-db-ja.com

データベース内のすべてのテーブルの非圧縮サイズを見つける

Dynamics AXには、テーブルをメモリにロードしてキャッシュするように構成できるキャッシュメカニズムがあります。このキャッシュは、メモリの問題を防ぐために、特定の量のKBに制限されています。私が話している設定はentiretablecacheと呼ばれ、単一のレコードが要求されるとすぐにテーブル全体をメモリにロードします。

最近まで、いくつかのスクリプトを使用して、この設定を持つテーブルのサイズを確認し、テーブルのサイズがこの制限を超えているかどうかを確認していました。

ただし、圧縮が機能し、 sp_spaceused または sys.allocation_units のようなものが、圧縮されたデータによって実際に使用されている領域を報告しているようです。

明らかに、アプリケーションサーバーは非圧縮データを処理しているため、SQL Serverのディスク上のデータサイズは関係ありません。非圧縮データの実際のサイズが必要です。

sp_estimate_data_compression_savings は知っていますが、名前が示すように、これは単なる見積もりです。
できるだけ正確なサイズにしたいと思います。

考えられる唯一の方法は、複雑な動的SQLが圧縮テーブルと同じ構造の非圧縮テーブルを作成し、そのシャドウテーブルに圧縮データを挿入してから、そのシャドウテーブルのサイズを確認することでした。
言うまでもなく、これは少し面倒であり、数百GBのデータベースで実行するには時間がかかります。

Powershellをオプションにすることもできますが、すべてのテーブルを繰り返し処理してselect *を実行し、スクリプトのサイズを確認すると、キャッシュがいっぱいになるだけで、おそらく時間がかかります。

簡単に言えば、可能であれば、各テーブルのサイズを取得する方法が必要です。これは、一度圧縮解除され、アプリケーションに提示される方程式から断片化されるためです。私はさまざまなアプローチを受け入れていますが、T-SQLが推奨されますが、Powershellまたは他の創造的なアプローチに反対していません。

アプリケーションのバッファがデータのサイズであると想定します。 bigintは常にbigintのサイズであり、文字データ型は1文字(ユニコード)あたり2バイトです。 BLOBデータもデータのサイズを取ります。列挙型は基本的にintであり、数値データは数値(38,12)です。datetimeは日時のサイズです。また、NULL値はなく、空の文字列1900-01-01またはゼロとして格納されます。

これがどのように実装されているかについてのドキュメントはありませんが、前提条件は、PFEとサポートチームが使用するいくつかのテストとスクリプトに基づいています(チェックはアプリケーションに組み込まれており、アプリが判別できないため、圧縮も明らかに無視します)基になるデータが圧縮されている場合)テーブルサイズもチェックします。 このリンク 例の状態:

大きなテーブルにEntireTableキャッシュを使用しないでください(AX 2009では128 KBまたは16ページ以上、AX 2012では「テーブルキャッシュサイズ全体」アプリケーション設定(デフォルト:32KB、または4ページ))-代わりにレコードキャッシュに移動します。

非圧縮データの実際のサイズが必要です。
...
できるだけ正確なサイズにしたいと思います。

この情報への欲求は確かに理解できますが、特に「可能な限り正しい」という状況でこの情報を取得することは、誤った仮定のために誰もが予想しているよりも困難です。質問で言及されている非圧縮シャドウテーブルのアイデアを実行しているか、DBの復元とそこへの圧縮解除に関するコメントでの@sp_BlitzErikの提案を実行しているかにかかわらず、非圧縮テーブルのサイズ==メモリ内の上記データのサイズとは見なされません。アプリサーバー上:

  1. all行がキャッシュされていますか?それとも範囲内ですか?ここでの仮定はそれがすべてであり、それが正しいかもしれないということですが、少なくとも、mightがそうではないことを言及する必要があると考えました(ドキュメントに特に記載がない限り、これはとにかくマイナーポイント、それが言及されたくないだけでした)。

    質問が次の状態に更新されました:はい、すべての行がキャッシュされています。

  2. 構造オーバーヘッド

    1. DB側:
      DB側のページと行オーバーヘッド:1ページにいくつの行が収まるかは、見積もりを失う可能性のある多くの要因によって決まります。 FILLFACTORが100(または0)であっても、行全体に十分ではないため、ページに未使用のスペースが残っている可能性があります。そしてそれはページヘッダーに追加されます。また、スナップショット分離機能が有効になっている場合は、バージョン番号が1行あたり13バイト余分に使用するため、見積もりが失われると思います。行の実際のサイズに関連する他の特徴点(NULLビットマップ、可変長列など)がありますが、これまでに説明した項目だけでポイントになります。
    2. アプリサーバー側:
      キャッシュされた結果を保存するためにどのような種類のコレクションが使用されていますか?これは.NETアプリだと思いますが、DataTableですか?一般的なリストですか? SortedDictionary?コレクションのタイプごとに、耳に聞こえる量が異なります。特に大規模な場合、DB側のページと行のオーバーヘッドを必ずしもミラーリングするオプションはないと思います(少量の行では問題になるほど十分ではないかもしれませんが、違いを探していません。数百バイトまたはほんの数キロバイト)。
  3. データ型
    1. DB側:
      CHAR/VARCHARデータは、文字あたり1バイトで格納されます(現時点では2バイト文字は無視されます)。 XMLは、テキスト表現が意味するほど多くのスペースを取らないように最適化されています。このデータ型は、要素と属性の名前のディクショナリを作成し、ドキュメント内のそれらへの実際の参照をそれぞれのIDで置き換えます(実際にはちょっといいです)。それ以外の場合、NCHAR/NVARCHARと同様に、文字列値はすべてUTF-16(「文字」あたり2または4バイト)です。 _DATETIME2_は6〜8バイトです。 DECIMALは5〜17バイトです(精度によって異なります)。
    2. アプリサーバー側:
      文字列(ここでも、.NETを想定)は常にUTF-16です。 VARCHARが保持するような8ビット文字列の最適化はありません。ただし、文字列は「インターン」することもできます。これは、何度も参照できる共有コピーです(ただし、これがコレクション内の文字列に対して機能するかどうか、またはすべてのタイプのコレクションに対して機能するかどうかはわかりません)。 XMLは、メモリに同じ方法で格納される場合とされない場合があります(これを調べる必要があります)。 DateTimeは常に8バイトです(T-SQL DATETIMEと同様ですが、DATETIME、または_DATETIME2_とは異なります)。 Decimal常に16バイト です。

つまり、アプリサーバー側でfairly正確なメモリフットプリントサイズを得るために、DB側でできることはほとんどありません。特定のテーブルをロードした後、アプリサーバー自体に問い合わせる方法を見つける必要があるので、それがどれほど大きいかを知ってください。そして、デバッガーが、満たされたコレクションのランタイムサイズを表示できるかどうかはわかりません。そうでない場合、閉じるには、テーブルのすべての行を調べ、各列に適切な。NETサイズを乗算する(例: INT = _* 4_、VARCHAR = DATALENGTH() * 2NVARCHAR = DATALENGTH()XML = ????など)ですが、それでもコレクションのオーバーヘッドとコレクションの各要素の問題が残ります。

質問にいくつかの新しい定義が与えられた場合、おそらく次のクエリを実行してかなり近づけることができます。そして、テーブルが圧縮されているかどうかは関係ありませんが、すべての行のスキャンが本番環境で適切かどうかを判断するのは各自の責任です(おそらく、復元から、またはオフピーク時に行います)。

_SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation
_

ただし、これはコレクションまたはコレクション要素のオーバーヘッドを考慮していません。デバッガーなしでその値を取得できるかどうか(またはILSpyのようなものかもしれませんが、mightが現地の法律に応じてEULAに違反しているため、私はそれをお勧めしません)。

7
Solomon Rutzky

あなたの質問から、あなたは最大キャッシュサイズSがあり、そのサイズを超えるキャッシュにテーブルをロードしたくないようです。それが本当なら、各テーブルの正確なサイズを知る必要はありません。テーブルが最大キャッシュサイズSより大きいか小さいかを知る必要があるだけです。テーブルの列定義と行数によっては、これは非常に簡単な問題です。

圧縮されていないデータを調べるのは適切な方法ではなく、キャッシュ内のテーブルの実際のサイズを適切に概算することが難しい場合があるという点で、Solomon Rutzkyの素晴らしい答えに同意します。ただし、質問の枠組みの中で作業し、静的データ型の列定義と動的列の実際の長さに基づいて十分に近い数式を作成できると想定します。

キャッシュサイズへのデータタイプのマッピングがある場合、テーブル内のデータを見ることさえせずに、いくつかのテーブルを評価できるはずです。

  1. テーブルに静的データ型のみ(文字列やBLOBではない)がある場合は、_sys.partitions_を見て行数を概算し、列定義を使用してテーブルのサイズを計算できます。
  2. たくさんの行があるテーブルに十分な静的データ型の列がある場合、データを見ないでそれを大きすぎるものとして排除できるかもしれません。たとえば、1,000万行と5個のBIGINT列を持つテーブルでは、10000000 *(8 + 8 + 8 + 8 + 8)= 400 Mバイトのサイズのデータ​​があり、キャッシュサイズの制限S。文字列列がたくさんあるかどうかは関係ありません。
  3. 行数が少ないテーブルが十分に小さい場合は、各動的データ型が可能な最大サイズであると仮定するだけで、それが制限を下回っていることを確認できる場合があります。たとえば、BIGINT列とNVARCHAR(20)列を持つ100行のテーブルは、100 *(8 + 2 * 20)= 4800バイトを超えることはできません。
  4. SQL Serverでテーブルの圧縮サイズがSの因数だけ大きい場合、キャッシュに収まる可能性が非常に低いことは事実です。このような値が存在するかどうかを調べるには、テストを行う必要があります。
  5. すべての動的列が偶然それらの統計を持っているという点で幸運になるかもしれません。統計には、平均の長さに関する情報が含まれており、目的に対して十分に正確である場合があります。

上記の基準のいずれにも適合しないテーブルのデータをクエリする必要がある場合があります。これによるパフォーマンスへの影響を最小限に抑えるために使用できるいくつかのトリックがあります。ここでは、競合する優先順位が2つあると思います。正確性を重視しますが、データベース内のすべてのデータをスキャンする必要もありません。ある種のバッファを計算に追加することが可能な場合があります。 Sの最大キャッシュサイズをわずかに下回るテーブルを除外するのか、最大キャッシュサイズをわずかに上回るテーブルを含めるのが適切かはわかりません。

以下は、テーブルデータをより速く参照するクエリを作成するためのアイデアです。

  1. 大きなテーブルの場合、サンプルサイズが十分に大きければ、TABLESAMPLEを使用できる場合があります。
  2. クラスター化されたキーを持つ大きなテーブルの場合、クラスター化されたキーでそれらをバッチで処理すると便利な場合があります。残念ながら、その集計の値に基づいて早期に終了するSUM()を計算する方法を知りません。私はこれまでROW_NUMBER()で機能することを見たことがあります。ただし、テーブルの最初の10%をスキャンし、計算されたデータサイズを節約し、次の10%をスキャンする、などとすることができます。キャッシュに対して大きすぎるテーブルの場合、このアプローチを使用すると、早期に終了することでかなりの量の作業を節約できる可能性があります。
  3. 一部のテーブルでは、すべての動的列のインデックスをカバーできるほど幸運である場合があります。行のサイズやその他の要因によっては、一度に各インデックスをスキャンする方が、テーブルスキャンを実行するよりも高速になる場合があります。単一の列のインデックスを読み取った後、テーブルのサイズが大きすぎる場合は、このプロセスを早期に終了することもできます。
  4. 動的列の平均の長さは、時間の経過とともにあまり変化しない可能性があります。計算する平均の長さを節約し、しばらくの間それらの値を計算に使用するのが現実的かもしれません。これらの値は、テーブル内のDMLアクティビティに基づいて、または他のいくつかのメトリックに基づいてリセットできます。
  5. すべてのテーブルに対してテストを実行してアルゴリズムを開発することが可能な場合は、データのパターンを利用できる場合があります。たとえば、テーブルを最初に小さいものから処理すると、10個(この数​​を増やした)のテーブルを一度に処理して、キャッシュに対して大きすぎると、大きなテーブルがすべてに収まる可能性は非常に低くなります。キャッシュ。キャッシュに収まる可能性のあるいくつかのテーブルを除外しても問題ない場合は、これで十分です。

この回答にはSQLコードが含まれていないことに気づきました。ここで説明したアイデアのデモコードを作成するのに役立つかどうかをお知らせください。

6
Joe Obbish