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


12

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

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

しかし、今では圧縮が作用し始めており、sp_spaceusedsys.allocation_unitsのようなものが、圧縮されたデータによって実際に使用されているスペースを報告しているようです。

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

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

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

Powershellはオプションの可能性がありますが、すべてのテーブルを反復処理しselect *てスクリプトでサイズを確認するのは好ましくありません。

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

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

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

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


3
それはハックですが、おそらく圧縮を無効にして復元されたコピーが最も正確でしょう。次に、リストアもテストしているため、トップ1のDBAのように見えます。
エリックダーリン

それがあなたの最善策だと信じています。並べ替えて数学を試す方法があります。定義済みの列データ型と長さによる行数を乗算してからインデックスなどに追加します。復元をスクリプト化し、@ sp_BlitzErikが上記で提案した圧縮を無効にするよりもはるかに手間がかかります。そして、トップ1のDBAになりたくないのは誰ですか?
マイクウォルシュ

すべての列のSUM(datalength())は非圧縮データサイズを取得しますか?
タパカUa

@sp_BlitzErikそれはコメントではなく答えかもしれません。
トムV-topanswers.xyzを試す

回答:


7

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

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

  1. されているすべてのテーブルの行がキャッシュされていますか?それとも範囲内ですか?ここでの仮定はすべてであり、それは正しいかもしれないが、少なくともそうでないかもしれないと言及すべきであると考えた(ドキュメントに別の記述ない限り、しかしこれはとにかくマイナーなポイントであり、ただ望んでいなかった)言及されていないこと)。

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

  2. 構造オーバーヘッド

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

つまり、DB側ではアプリサーバー側でかなり正確なメモリフットプリントサイズを得るためにできることはほとんどありません。特定のテーブルを読み込んだ後、アプリサーバー自体に問い合わせる方法を見つける必要があるので、その大きさを知ってください。そして、デバッガーがいっぱいになったコレクションのランタイムサイズを表示できるかどうかはわかりません。そうでない場合、近づくための唯一の方法は、テーブルのすべての行を調べて、各列に適切な.NETサイズ(たとえば、INT= * 4VARCHAR= 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のようなものかもしれませんが、現地の法律によってはEULAに違反する可能性があるため、お勧めしません)。


最終的に、アプリケーションに表示されるバッファサイズを確認するために、コードのチェックを実装しました。
トムV-topanswers.xyz

6

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

Solomon Rutzkyの素晴らしい答えに同意します。非圧縮データを調べることは進むべき道ではなく、キャッシュ内のテーブルの実際のサイズを適切に近似することは難しいかもしれません。ただし、質問のフレームワーク内で作業し、静的データ型の列定義と動的列の実際の長さに基づいて十分に近い式を開発できると仮定します。

キャッシュタイプへのデータタイプのマッピングがある場合、テーブル内のデータを見なくてもいくつかのテーブルを評価できるはずです。

  1. テーブルに静的データ型のみ(文字列またはBLOBなし)があるsys.partitions場合、列定義を使用してテーブルのサイズを調べて計算することにより、行数を概算できます。
  2. 多数の行を持つテーブルに十分な静的データ型の列がある場合、そのデータを見なくても大きすぎるテーブルを削除できる場合があります。たとえば、1000万行と5 BIGINT列のテーブルでは、そのデータのサイズが10000000 *(8 + 8 + 8 + 8 + 8)= 400 Mバイトになり、キャッシュサイズの制限より大きくなる可能性がありますS。文字列列がたくさんあるかどうかは関係ありません。
  3. 行数の少ないテーブルが十分に小さい場合、各動的データ型の最大サイズを想定するだけで、制限を下回っていることを確認できる場合があります。たとえば、BIGINT列と列を持つ100行のテーブルは、NVARCHAR(20)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コードが含まれていなかったことがわかります。ここで説明したアイデアのいずれかのデモコードを作成すると役立つかどうかを教えてください。


2
私はそのようなテーブルを除外するアプローチを考えていませんでした、私はアプローチが好きです
トムV-topanswers.xyzを試してください17年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.