ギャップロック
ギャップロック: これはインデックスレコード間にあるギャップのロック、または先頭のインデックスレコードの前や末尾のインデックスレコードのあとにあるギャップのロックです。
shimizukawa.icon要約
REPEATABLE READ トランザクション分離レベルでは、フォントムリードを避けるため、参照中のインデックスをロックすることがある。このとき、範囲の固定化されていない領域(ギャップ)をロックすることをギャップロックという。
発生するパターン
SELECT で発生する例
SELECT * FROM table WHERE id=1 FOR UPDATE を使った場合は発生させられる
FOR UPDATE を使わずにSELECTするだけなら発生しない?
INSERT + UPDATE で発生する例
回避方法
サービス開始後にこれを行うのは、影響範囲が広いためなかなか難しそう
発生するパターンのSQL発行を避ける
ORM使ってると気づかないうちに発生するかも
Djangoの場合 obj.save() が空振りUPDATEを発行することがある
回避1 model.objects.create() で作成する
回避2 obj.save(force_insert=True) を指定する
トランザクションの使用範囲を狭くする
トランザクション中に「発生するパターン」に書いたSQLを発行するのを避けるため
Djangoで発生するギャップロックの例
継承テーブルを題材にしているけど、それは本質的な部分ではない
UPDATEの空振りでギャップロックが発生する部分に注目
deadlockを引き起こしたSQLがなにかを調査する方法が書かれている
良い資料