SQL Server のデッドロックは、「お互いのロックを奪い合って進める」状態。エラー 1205 で一方が犠牲者(victim)になる。
この記事では、見方とざっくりした対策を逆引きできるようにまとめる。
先に結論
- デッドロックは例外というより競合の結果
- まずは「どの資源を、どの順でロックしたか」を見る
- 対策はインデックス、アクセス順の統一、トランザクション短縮が基本
症状
- エラー 1205(トランザクションがデッドロックの犠牲)
- 特定処理の組み合わせでのみ起きる
- 負荷が上がると急に増える
見方(ざっくり)
- 拡張イベント / デッドロックグラフで関係セッションを確認
- 犠牲になったクエリと相手クエリを両方見る
- ロック対象(キー、ページ、オブジェクト)と順番を確認
アプリ側では、1205 をリトライ対象にする設計もよくある。ただし根本原因の放置は推奨しない。
よくある原因
- トランザクションが長い(ロック保持時間が長い)
- 同じ資源へのアクセス順が処理によって違う
- インデックス不足でロック範囲が広い
- 更新と参照がぶつかりやすいクエリ形状
対策の型
- トランザクションを短くする(余計な処理を外に出す)
- 更新対象の取得順をアプリ/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点でロック競合を減らす
- リトライは応急、設計改善が本丸