ロギング用のテーブルと、veryDELETEのパフォーマンスが非常に遅い古いデータをパージするためのストアドプロシージャがあります。テーブルまたはDELETEステートメントを変更して、LOBデータに対して適切に実行する方法を探しています。または、Microsoftから問題の承認がある場合-「SQLサーバーのバージョンxでこれを修正しました」、または「これはパフォーマンスが悪いと思われますが、優先順位ではありません」のようなものでも、それはうまくいきます。
これは、Microsoft SQL Server 2012(SP3)で実行されています。以下は実質的に私の実際のテーブルとコードですが、わずかに単純化されています:
CREATE TABLE [LOG_VALUE](
[ID] [int] IDENTITY(1,1) NOT NULL,
[VALUE] [varchar](max) NOT NULL,
[CHECKSUM] [int] NOT NULL,
[VALUE_LEN] [int] NOT NULL,
CONSTRAINT [PK_LOG_REQUEST] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
問題の削除はこれで行われます:
WHILE (@@ROWCOUNT > 0)
DELETE TOP (100) [LOG_VALUE]
OUTPUT DELETED.[VALUE_LEN] INTO @DELETED_ROWS
WHERE [ID] IN (SELECT [ID] FROM @DELETE_IDS);
基本的なストアドプロシージャプロセスは次のとおりです。
LOBデータを削除するには、LOBデータをサーバーのメモリにロードする必要があるようです。サポート:1)テーブルに12,000行を挿入すると、ほぼ瞬時に2)12,000行を取得するのがほぼ瞬時に3)それらがキャッシュに存在しなくなると、同じ12,000行を選択して削除すると、約40時間がかかります。秒。
私が見つけることができた最も関連する質問は次のとおりです: https://serverfault.com/questions/241893/delete-performance-for-lob-data-in-sql-server 2011から、しかし悲しいことに満足のいく答えはありません。これをdba.stackexchange.comで尋ねると、より知識のあるオーディエンスが見つかることを願っています:)その質問の著者は間違いなく私よりもこれについて知っていましたが、私が読んだときの中心的なポイントは次のとおりです。
この割り当て解除は、クリーンアップタスクで行われるべきであり、事前に行われるべきではないようです。また、LOBデータは述部(またはOUTPUT)で参照されないため、キャッシュにロードする必要はまったくありません。
関連があるかもしれないより多くの情報が、私はそれがそうではないと思います:
READ_COMMITTED_SNAPSHOT
およびALLOW_SNAPSHOT_ISOLATION
は両方ともオフです。データベース全体(および同じサーバー上の他のデータベース)は、LOBデータの削除/更新以外のすべての場合に期待どおりに動作します。
...それらがキャッシュに存在しなくなると、同じ12,000行を選択して削除するのに約40秒かかります。
これは、ストレージサブシステムが不十分であることを示しているようです。これが原因である場合、SQL ServerはおそらくPAGEIOLATCH_XX
待機タイプ。
LOBデータを削除するには、LOBデータをサーバーのメモリにロードする必要があるようです。
Paul Randalが言ったと報告されているように(更新2、リンクした質問では)、SQL Server must LOBツリーをトラバースしてlocate the pagesでなければなりません削除されました。これを行うには、SQL ServerがこれらのLOBツリーページをメモリに取り込む必要があります。これを回避する方法は本当にありません。情報は他の方法では利用できません。
この割り当て解除は、クリーンアップタスクで行われるべきであり、事前に行われるべきではないようです。
これが機能する方法は、十分に文書化されていません。
私のテストでは、SQL Serverは、行を保持する特別な理由がない場合、LOBデータをすぐに削除します。これは最適化のようです。結局のところ、既にLOBデータを操作している場合は、そこにいる間に削除することもできます。ゴーストクリーンアップ(GC)に延期しても、GCはチェーンを走査する必要があるため、オーバーヘッドが増えるだけです。さらに、ログが削減されます。これは、SQL Serverが割り当て解除を記録するだけでよく、完全なLOBコンテンツは記録しないためです。
(たとえば)テーブルにトリガーがある場合、または行バージョンの分離レベルが有効になっている場合、LOB削除操作はdeferredto GCです。これらの機能はどちらも行バージョンを使用しており、LOB行で維持する必要があります。これらの場合、LOB削除は完全にログに記録されます。 GCが後で実行されるときに、LOBページの割り当て解除が実行されます。 ( documented )グローバルトレースフラグ661を使用してGCを一時的に無効にして、自分でテストする場合にこれらのゴースト化されたLOBレコードを表示できます。
これらすべてを考慮すると、削除をログに記録し、GCに対して既に行われた同じ作業の繰り返しを(完全に)延期することがどのように役立つかを理解するのは困難です。削除中に記録するログの量を減らしてLOBデータを削除する方が高速です。
質問には、削除パフォーマンスの低下の正確な原因について推測するだけでは不十分です。圧倒されたり、指定が不十分だったりするストレージシステムの可能性はありませんが、一般的には次のことをお勧めします。
ポイント1の詳細については、Paul Randalによる パフォーマンスのバグ:行外LOBデータを含むNOLOCKスキャン を参照してください。ポイント2と3の詳細については、 ページを分割する削除と転送されたゴースト (私による)を参照してください。
同様に、単純化された例では重要な詳細が欠落している、テーブル変数が非効率的な実行計画を作成している、またはテーブルに数十億のゴーストLOBレコードがすでに存在している可能性があります。詳細な分析には、おそらくシステム自体へのアクセスが必要になります。