※この記事は推定が含まれます。
確実な裏付けをとっているわけではないことに注意してください。
間違っている点があればドンドン指摘してくださると嬉しいです。
発生していた問題
単純なinsert文でデッドロックが発生していた。
トランザクション内では問題が起きているinsert文しか実行していない。
ありがちな「複数のテーブルを更新する場合の更新順序のズレでのデッドロック」ではない。
正直目を疑ったけれど、これが現実なのよね……
DBテーブルの状態
DB
SQL Server
トランザクション分離レベル
デフォルトの "READ COMMITTED"
トリガー
after insert
as
update hogehoge set insdate=getdate() where id in (select id from inserted)
after update
as
update hogehoge set upddate=getdate() where id in (select id from inserted)
インデックス
インデックス種類 | 対象カラム |
---|---|
クラスタ化インデックス | id |
非クラスタ化インデックス1 | insdate |
非クラスタ化インデックス2 | upddate |
発生原因の推定
おそらくだけれど、トリガーが原因ではないだろうかと推定。
以下の流れでデッドロックが発生していると思われる。
- 2つの画面(接続1と接続2)からほぼ同時に追加処理が呼び出される
- 接続1でinsert実行
- 接続2でinsert実行
- 接続1でトリガ実行。しかし、接続2でレコードが追加されているため接続2の開放待ち※1
- 接続2でトリガ実行。上と同様の理由で接続1の開放待ち
- デッドロック\(^o^)/
※1
実行されるupdate文でwhereで更新対象を指定している。
他の接続で追加されたレコードが対象に含まれるかもしれないため、他の接続の開放待ちとなる。
対策
トリガを辞めます。
今回問題が発生しているSQLを含め、hogehogeテーブルに追加更新しているSQLそれぞれで明示的にinsdate、upddateの値を設定するようにします。
……他の対策があればいいのだけど、見つからないのだよ
トリガをやめてみたところ、デッドロックは発生しなくなりました。
これで原因があっていたのだろう。
未調査、未確認の内容
その他の対策として
今思えば、トランザクション分離レベルを "READ_COMMITTED_SNAPSHOT" にすれば良いだけのような気もします。
その他の原因
また、SQLserverではクラスタインデックス、非クラスタインデックス間でのデッドロックが発生することがあるらしい。
SQL SERVERにおけるデッドロック(内部仕様)〜クラスタ化インデックスと非クラスタ化インデックス間のデッドロック〜
今回もこれが発生した可能性も無きにしもあらず?
(でも、上記の対応でデッドロックが起きなくなったので、これは関係ないように見える)