スコープ
MySQL / InnoDB
ネクストキーロックとは
例えば、以下のコマンドを実行すると、100以上のidを挿入・更新できなくなりそうだが、そうとは限らない。
SELECT id FROM tbl_name WHERE id > 99 FOR UPDATE;
理解するヒントは、InnoDBは、式にロックを掛けるのではなく、「行」と「ギャップ」にロックを掛けるという点にある。
「行」とは実際にテーブルに保存されている行のことである。「ギャップ」とは、行と行の間を表す空間である。
実際のInoDBの挙動としては、「SELECTした行」と「その行の前後のギャップ」にロックを掛けるのである(行ロック・ギャップロック)。行ロックとギャップロックを合わせて「ネクストキーロック」という。以下具体例を使って説明する。
具体例
テーブルに以下のデータが入っているケースを考える。
+------------+
| id |
+------------+
| 105 |
| 95 |
+------------+
このケースで冒頭のクエリを実行した場合、ロックが掛かる範囲は以下の通りである。
-
id=105
の行 -
id=105
の行の前後のギャップ、すなわち-
id=95 ~ 105
のギャップ -
id=105 ~ inf
のギャップ、すなわち id=105 より大きい全ての空間
-
よって、 id=105
の行は更新できないし、 id=106
の挿入もできない。が、それに加えて、 id=96
のような値も挿入できなくなる。
冒頭の式の挙動の回答としては、具体的にどのような値の挿入がロックされるかは、テーブルのレコードによる、ということになる。式では WHERE id > 99
と指定しているが、それより小さい値を挿入できなくなることもあるということである。
これがネクストキーロックというものの特性である。
補足情報
- id=95の行にはロックがかからないので、この行を更新することはできる
- ネクストキーロックが掛かる条件については公式情報を参照してもらうのがよいが、ざっくり言うと、一意の行を指定してロックを掛ける場合を除き全てのクエリで掛かる
- 例えば上記の例の場合、
WHERE id=105
で指定するとネクストキーロックは掛からず、id=105の行のみにロックが掛かる - 一方、
WHERE id=99
で指定すると、id=95~105
の間にギャップロックがかかり、例えばid=100
などを挿入できなくなる
- 例えば上記の例の場合、
- トランザクション分離レベルを READ COMMITTED に変更すると、ネクストキーロックは無効になる
- ネクストキーロックは、トランザクション分離レベルに密接した概念であり、REPEATABLE READ(InnoDBのデフォルト)では防げないファントムリードを防ぐためにある。
- 以上のルールは、SELECT FOR UPDATEだけでなく、トランザクション中で実行したUPDATE, DELETE文でも同様である