私は、システムの 1 つで繰り返される「バグ レポート」(パフォーマンスの問題) を分析しており、特に遅い削除操作に関連しています。簡単に言えば、キーが主な原因であると思われCASCADE DELETE
ます.(a)これが理にかなっているのか、(b)なぜそうなのかを知りたい.
たとえば、関連テーブルや関連テーブルなどの大きなグラフのルートにあるウィジェットなどのスキーマがあります。完全に明確にするために、このテーブルから削除することは積極的に推奨されていません。それは「核の選択肢」であり、ユーザーは逆に幻想を抱くことはありません。とはいえ、やらなければならないこともあります。
スキーマは次のようになります。
Widgets
|
+--- Anvils [1:1]
| |
| +--- AnvilTestData [1:N]
|
+--- WidgetHistory (1:N)
|
+--- WidgetHistoryDetails (1:N)
列の定義は次のようになります。
Widgets (WidgetID int PK, WidgetName varchar(50))
Anvils (AnvilID int PK, WidgetID int FK/IX/UNIQUE, ...)
AnvilTestData (AnvilID int FK/IX, TestID int, ...Test Data...)
WidgetHistory (HistoryID int PK, WidgetID int FK/IX, HistoryDate datetime, ...)
WidgetHistoryDetails (HistoryID int FK/IX, DetailType smallint, ...)
怖すぎることはありません。aWidget
は異なる型にすることができ、 anAnvil
は特別な型であるため、関係は 1:1 (より正確には 1:0..1) になります。次に、硬度、腐食、正確な重量、ハンマーの互換性、使いやすさの問題、および漫画の頭での衝撃テストを扱う、大量のデータがありますAnvilTestData
。Anvil
次に、それぞれWidget
に、生産、在庫の移動、販売、欠陥調査、RMA、修理、顧客からの苦情など、さまざまな種類のトランザクションの長くて退屈な履歴があります。その年齢に応じて。
したがって、当然のことながら、CASCADE DELETE
ここにはあらゆるレベルで関係があります。を削除する必要がある場合Widget
、それは何かがひどく間違っていることを意味し、履歴、テスト データなどを含む、そのウィジェットの既存の記録をすべて消去する必要があります。
関係はすべて索引付けされ、統計は最新です。通常のクエリは高速です。システムは、削除を除くすべての処理で非常にスムーズに動作する傾向があります。
最後に、さまざまな理由から、一度に 1 つのウィジェットしか削除できないため、delete ステートメントは次のようになります。
DELETE FROM Widgets
WHERE WidgetID = @WidgetID
非常にシンプルで無害に見える削除...データのないウィジェットの場合、実行に2分以上かかります!
実行計画をたどった後、最終的にコストが最も高いサブ操作としてAnvilTestData
と削除を選択することができました。WidgetHistoryDetails
そこで、 をオフにしてCASCADE
(ただし、実際の FK を に設定するだけでNO ACTION
)、スクリプトを次のように書き直して実験しました。
DECLARE @AnvilID int
SELECT @AnvilID = AnvilID FROM Anvils WHERE WidgetID = @WidgetID
DELETE FROM AnvilTestData
WHERE AnvilID = @AnvilID
DELETE FROM WidgetHistory
WHERE HistoryID IN (
SELECT HistoryID
FROM WidgetHistory
WHERE WidgetID = @WidgetID)
DELETE FROM Widgets WHERE WidgetID = @WidgetID
これらの「最適化」はどちらも大幅なスピードアップをもたらし、それぞれが実行時間をほぼ 1 分短縮したため、元の 2 分間の削除は約 5 ~ 10 秒かかりました - 少なくとも新しいウィジェットの場合、多くの履歴やテストは必要ありませんデータ。
完全に明確にするために、ファンアウトが最も高いCASCADE
from からWidgetHistory
までがまだあります。ここでは からのものだけを削除しました。WidgetHistoryDetails
Widgets
カスケード関係をさらに「フラット化」すると、徐々に劇的ではなくなりますが、それでも顕著なスピードアップが見られ、より大きなテーブルへのカスケード削除がすべて削除され、明示的な削除に置き換えられると、新しいウィジェットの削除がほぼ瞬時に行われるようになりました。
私は各テストの前にDBCC DROPCLEANBUFFERS
andを使用しています。DBCC FREEPROCCACHE
さらにスローダウンを引き起こす可能性のあるすべてのトリガーを無効にしました (ただし、それらはいずれにせよ実行計画に表示されます)。また、私は古いウィジェットに対してもテストを行っており、そこでも大幅な高速化が見られます。以前は 5 分かかっていた削除が 20 ~ 40 秒で完了します。
現在、私は「SELECT は壊れていない」という哲学の熱烈な支持者ですが、この動作について論理的な説明はないようですCASCADE DELETE
。
だから、私の質問は次のとおりです。
これは SQL Server の DRI に関する既知の問題ですか? (Google や SO でこの種のことへの言及を見つけることができなかったようです。答えはノーだと思います。)
そうでない場合、私が見ている動作について別の説明がありますか?
これが既知の問題である場合、なぜそれが問題なのですか? また、使用できるより良い回避策はありますか?