BLOBを別のSQL Serverテーブルに保存することが推奨されるのはなぜですか?


28

この非常に支持されたSOの回答では、別のテーブルとの1:1関係しかない場合でも、画像を別々のテーブルに配置することを推奨しています。

写真をSQL Serverテーブルに配置する場合、写真を保存するために別のテーブルを使用することを強くお勧めします。従業員の写真を従業員のテーブルに保存せずに、別のテーブルに保管してください。このように、従業員のテーブルは、クエリの一部として従業員の写真も常に選択する必要がないと仮定すると、無駄がなく、平均的で非常に効率的です。

どうして?SQL Serverはテーブルに専用のBLOBデータ構造へのポインターのみを格納しているのではないかという印象を受けましたが、なぜ別の間接層を手動で作成する必要があるのですか?それは本当にパフォーマンスを大幅に改善しますか?はいの場合、なぜですか?

回答:


15

私は、BLOBが別のテーブルにあるだけでなく、データベースにあるべきではないことに同意しませ。ファイルがディスク上に存在する場所へのポインタを保存し、データベースから取得します...

(私にとって)それらが引き起こす主な問題は、インデックス作成にあります。クエリプランでXMLを使用します。誰もが理解しているため、テーブルを作成しましょう。

SELECT TOP 1000
ID = IDENTITY(INT,1,1),
deq.query_plan
INTO dbo.index_test
FROM sys.dm_exec_cached_plans AS dec
CROSS APPLY sys.dm_exec_query_plan(dec.plan_handle) AS deq

ALTER TABLE dbo.index_test ADD CONSTRAINT pk_id PRIMARY KEY CLUSTERED (ID)

それはわずか1000行ですが、サイズを確認しています ...

sp_BlitzIndex @DatabaseName = 'StackOverflow', @SchemaName = 'dbo', @TableName = 'index_test'

わずか1000行で40 MBを超えています。1000行ごとに40 MBを追加すると仮定すると、それは非常にいものになります。100万行をヒットするとどうなりますか?約1 TBのデータです。

ナッツ

必要性は、あなたのクラスタ化インデックスを使用することを任意のクエリは現在メモリにそのBLOBデータのすべてを読み込む必要があり明確化: BLOBデータ列が参照されたとき。

BLOBを保存するよりも、SQL Serverのメモリを使用するより良い方法を考えられますか?確かにできるから。

非クラスター化インデックスに拡張する:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

CREATE INDEX ix_returnoftheblob ON dbo.index_test (ID) INCLUDE (query_plan)

通常のクエリでクラスター化インデックスを回避できるように、非クラスター化インデックスを設計してBLOB列を大幅に回避できますが、そのBLOB列が必要になるとすぐにクラスター化インデックスが必要になります。

INCLUDEDキールックアップのシナリオを回避するために非クラスター化インデックスに列として追加すると、巨大な非クラスター化インデックスになります。ここに画像の説明を入力してください

彼らが引き起こすより多くの問題:

  • 誰もが実行している場合はSELECT *、クエリを、彼らはすべてのことBLOBデータを取得します。
  • バックアップと復元でスペースを占有し、速度が低下します
  • DBCC CHECKDBあなたは腐敗をチェックしていることを知っているので、彼らは遅くなりますか?
  • また、インデックスのメンテナンスを行うと、速度も低下します。

お役に立てれば!


7
ユーザーは通常SELECT *と入力するからです。
ブレントオザー

あなたが言及する欠点は、彼が写真を別のテーブルに置くことを勧めた理由の一部だと思います。ユーザーに関するさまざまなレポートを実行している場合、ユーザーの画像ファイルは必要ありません。単一のユーザーのプロファイルページを読み込んでいる場合、blobテーブルに参加します。私はここで何かを見逃していますか(つまり、あなたが説明したこのシナリオでもあなたの欠点は実際に適用されますか?)
BVernon

11

これらの画像の大きさはどれくらいですか?私はほとんど@sp_BlitzErikに同意しますが、これを行うのは問題ないシナリオがあると思うので、実際にここで何が要求されているかをより明確に把握するのに役立ちます。

エリックが指摘したネガティブな側面のほとんどを軽減すると考えるいくつかのオプションは次のとおりです。

これらのオプションは両方とも、BLOBを完全にSQL Serverに格納するか、完全に外部に格納するかを仲介するように設計されています(パスを保持するための文字列コロンを除く)。BLOBをデータモデルの一部として使用し、バッファプール(メモリ)のスペースを無駄にせずにトランザクションに参加することができます。BLOBデータはまだそれらがより多くのスペースを取る作成し、バックアップに時間がかかるんバックアップに含まれている復元します。ただし、アプリの一部である場合は何らかの方法でバックアップする必要があり、パスを含む文字列列のみが完全に切断され、BLOBファイルを取得できるため、これを真のネガティブとみなすのは難しいDBにそのことを示すことなく削除されました(つまり、無効なポインター/欠落ファイル)。また、DB内でファイルを「削除」することもできますが、最終的にクリーンアップする必要があるファイルシステム上に残っています(つまり、頭痛)。ただし、ファイルが非常に大きい場合は、パス列を除いてSQL Serverの外部に完全に置くのが最善の方法です。

これは「内部または外部」の質問には役立ちますが、単一テーブルの質問と複数テーブルの質問には触れません。この特定の質問を超えて、使用パターンに基づいてテーブルを列のグループに分割するための有効なケースは確かにあると言えます。多くの場合、50以上の列がある場合、頻繁にアクセスされる列とそうでない列があります。一部の列は頻繁に書き込まれ、一部の列はほとんど読み取られます。頻繁にアクセスする列とアクセス頻度の低い列を1:1の関係を持つ複数のテーブルに分離することは、おそらく使用していないデータ用にバッファープールのスペースを無駄にする理由(大きな画像を通常の場所に保存する理由と同様)VARBINARY(MAX)列が問題です)?また、行サイズを小さくし、データページにより多くの行を収めることで、頻繁にアクセスする列のパフォーマンスを向上させ、読み取り(物理的および論理的)の効率を高めます。もちろん、PKを複製する必要があるため、いくつかの非効率性も生じます。また、2つのテーブルを結合する必要が生じる場合があります。

そのため、実行できるアプローチはいくつかありますが、最善の方法は環境と達成しようとしているものによって異なります。


SQL Serverはテーブルに専用のBLOBデータ構造へのポインタのみを格納するという印象を受けました

それほど単純ではありません。ここで、Varchar、Varbinaryなどの(MAX)タイプのLOBポインターのサイズはいくらですか?、しかし基本は次のとおりです。

  • TEXTNTEXT、およびIMAGEデータ型(デフォルトで):16バイトのポインタ
  • VARCHAR(MAX)NVARCHAR(MAX)VARBINARY(MAX)(デフォルトでは):
    • データが行に収まる場合、そこに配置されます
    • データが約未満の場合。40,000バイトは、(リンクされたブログ記事が示す上限として40,000私のテストでは若干高い値を示した)およびこの構造の行に余裕がある場合、始まる、LOBページに1と5の直接リンクとの間に存在します最初の8000バイトへの最初のリンクでは24バイト、追加の8000バイトの各セットでは、最大72バイトまでの追加リンクごとに12バイトずつ増加します。
    • データが約を超えている場合 40,000バイトまたは適切な数の直接リンクを格納するための十分なスペースがありません(たとえば、行に残っているのは40バイトのみで、20,000バイトの値には最初の24バイトに48バイトの2つの追加リンクの12の3つのリンクが必要です)合計必要な行内スペース)、LOBページへのリンクを含むテキストツリーページへの24バイトポインターがあります)。

7

何らかの理由でデータをSQL Serverに保存する必要がある場合、別のテーブルに保存することにはいくつかの利点があると思います。他の人よりも説得力のある人もいます。

  1. データを別のテーブルに入れると、別のデータベースに保存できます。これは、定期メンテナンスに利点があります。たとえばDBCC CHECKDB、BLOBデータを含むデータベースでのみ実行できます。

  2. 常に8000バイト以上をBLOBに入れるとは限らない場合、一部の行については行内格納することが可能です。クエリで列が不要な場合でも、クラスター化インデックスを使用してデータにアクセスするクエリの速度が低下するため、これは望ましくありません。データを別のテーブルに入れると、このリスクがなくなります。

  3. 行外に格納される場合、SQL Serverは最大24バイトのポインターを使用して新しいページを指します。これはスペースを占有し、1つのテーブルに追加できるBLOB列の総数を制限します。詳細については、srutzkyの回答を参照してください。

  4. BLOB列を含むテーブルでは、クラスター化列ストアインデックスを定義できません。この制限は削除されましたが、SQL Server 2017では削除されます。

  5. 最終的に、データをSQL Serverの外部に移動する必要があると判断した場合、データが既に別のテーブルにある場合、その変更を行う方が簡単な場合があります。


1
ここにいくつかの良い点があります(+1)。ただし、#3(オフ行データ用の24バイトポインター)について明確にするために、常に正しいとは限りません。データ型、値のサイズ、行の空き領域の量がポインターのサイズをどのように決定するかについて、回答の下部で(簡単に)説明します。
ソロモンラツキー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.