• システム開発に関わる内容をざっくりと書いていく

SQL Server デッドロックの見方と対策

SQL Server のデッドロックは、「お互いのロックを奪い合って進める」状態。エラー 1205 で一方が犠牲者(victim)になる。

この記事では、見方ざっくりした対策を逆引きできるようにまとめる。


先に結論

  • デッドロックは例外というより競合の結果
  • まずは「どの資源を、どの順でロックしたか」を見る
  • 対策はインデックス、アクセス順の統一、トランザクション短縮が基本

症状

  • エラー 1205(トランザクションがデッドロックの犠牲)
  • 特定処理の組み合わせでのみ起きる
  • 負荷が上がると急に増える

見方(ざっくり)

  • 拡張イベント / デッドロックグラフで関係セッションを確認
  • 犠牲になったクエリと相手クエリを両方見る
  • ロック対象(キー、ページ、オブジェクト)と順番を確認

アプリ側では、1205 をリトライ対象にする設計もよくある。ただし根本原因の放置は推奨しない。


よくある原因

  1. トランザクションが長い(ロック保持時間が長い)
  2. 同じ資源へのアクセス順が処理によって違う
  3. インデックス不足でロック範囲が広い
  4. 更新と参照がぶつかりやすいクエリ形状

対策の型

  • トランザクションを短くする(余計な処理を外に出す)
  • 更新対象の取得順をアプリ/SQL でそろえる
  • 適切なインデックスでロック範囲を狭める
  • 必要ならリトライ(冪等性に注意)
  • 隔離レベル変更は副作用が大きいので最終手段寄り

アプリ側のリトライ例(概念)

for (var i = 0; i < 3; i++)
{
try
{
await SaveAsync();
break;
}
catch (SqlException ex) when (ex.Number == 1205)
{
await Task.Delay(50 * (i + 1));
}
}

ざっくりまとめ

  • 1205 は犠牲者通知。相手クエリも見る
  • 順番・範囲・時間の3点でロック競合を減らす
  • リトライは応急、設計改善が本丸