web-dev-qa-db-ja.com

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

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

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

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

31
Heinzi

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=データの場合です。

NUTS

クラスタ化インデックスを使用する必要のあるすべてのクエリは、BLOBデータ列が参照されるときに、すべてのBLOBデータをメモリに読み込む必要がありますclarification:

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列として追加すると、巨大な非クラスター化インデックスになります: enter image description here

彼らが引き起こすさらなる問題:

  • 誰かがSELECT *クエリ、BLOBデータをすべて取得します。
  • バックアップとリストアでスペースを取り、速度を低下させる
  • 彼らは遅くなるDBCC CHECKDB、破損をチェックしていることを知っているので、そうですか?
  • インデックスのメンテナンスを行うと、速度も低下します。

お役に立てれば!

16
Erik Darling

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

Erikによって指摘された否定的な側面のほとんどを軽減することを検討するいくつかのオプションは次のとおりです。

これらのオプションはどちらも、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を示していますが、私のテストでは少し高い値を示しています)[〜#〜] and [〜#〜]この構造の行にスペースがある場合、LOBページへの1〜5の直接リンクがあり、最初のリンクの最初の8000バイトへの24バイトから始まり、追加のリンクごとに12バイトずつ増加します。 8000バイトの追加セットごとに、最大72バイト。
    • データが約を超えている場合。 40,000バイト[〜#〜]または[〜#〜]適切な数の直接リンクを格納するための十分なスペースがありません(例:残り40バイトのみ)行と20,000バイトの値には3つのリンクが必要です。これは最初の24バイトに2つの追加リンクの12バイトを加えた合計48バイトの行内スペースが必要です)、その後、テキストツリーページへの24バイトのポインターがあります。 LOBページへのリンクが含まれています)。
12
Solomon Rutzky

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

  1. データを別のテーブルに配置することは、別のデータベースにデータを格納できることを意味します。これには、定期メンテナンスの利点があります。たとえば、DBCC CHECKDB BLOBデータを含むデータベースでのみ。

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

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

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

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

8
Joe Obbish