謎のデッドロック

担当機能で、データベース(HiRDB)のデッドロックが起きた。
エラーログによると、テーブル上の論理的な一意キー(物理設計上の制約ではない)で排他ロックをかける SELECT 文にてデッドロックが発生している。

デッドロックは、2つのトランザクションでお互い他方が占有している資源を待ち、膠着状態になることを指す。
したがって、1レコードしかロックしないのであればデッドロックになるはずがない。

HiRDB が内部的に複数レコードをロックしている以外考えられない。

この状況は、ビジネスルール上の論理的なキーと物理設計上のキーが合致しないために生じる。

たとえば:

SELECT ... FROM X WHERE A = '1' AND B = '1'
X 表には、

  • A = '1' かつ B = '1' のレコード(1)
  • A = '1' かつ B = '2' のレコード(2)

があるとする(A はユニークでないインデックス列)。
上の SELECT 文を解析した DB は

  1. A のインデックスを取得。
  2. 探索条件 A = '1' に該当する ROWID を特定。
  3. 対象レコードロック。
  4. B 列の値を探索条件の B = '1' と比較する。
  5. マッチしているか?
    • マッチしていればクエリの結果セットとして返す。
    • マッチしていなければロックを解放する。
  6. 次対象行があれば 3 に戻る。

という動作を行う。

ポイントはテーブルアクセス時、対象レコードが判定中に更新されないよう DB が内部的にロックをかける点。ここでデッドロックが発生するようなら、SQL レベルで意識されないので厄介だ。