ロックの分類
InnoDBのロックは「タイプ1」「モード」「精密なモード2」で分類できる。
ロックのタイプ1(lock type)
ロックのタイプは2種類ある。InnoDBに限った概念ではない。
InnoDBではなるべくテーブルロックを使わず、行ロックを使うようにしているそうだ。実際、明示的にテーブルロックを利用しようという場面はほとんどないと思う。
たまに勘違いしている人をみかけるが、**「行ロックの範囲がテーブル全体である」ことと「テーブルロック」は異なる。**前者はあくまでも行ロックである。
システムによっては、メモリ節約のために「大量の行ロック」を「テーブルロック」に自動的に変更することもあるらしいが、InnoDBでは不要だそうだ。(参考:ロックのエスカレーション)
また、InnoDBでは、行ロックだけを使っているつもりでも内部的にはテーブルロックの仕組みが使われている。(詳しくはインテンションロックの項目を参照。)
ロックのモード(lock mode)
ロックのモードは2種類ある。InnoDBに限った概念ではない。
共有ロックは(S)
、排他ロックは(X)
と表記されることがある。
ロックの精密なモード2(precise mode)
ロックの精密なモードは、InnoDBに特有の概念(というより、実装方法からくる分類)。行ロックだけの分類で、テーブルロックには関係ない。
- レコードロックは、その名の通り、レコードをロックする。
- ギャップロックは、その名の通り、ギャップ(隙間)をロックする。レコードとレコードの間をロックすることで、その空間に新しいレコードが挿入されることを防ぐ。
- ネクストキーロックは、レコードロックと、そのレコードの前の空間に対するギャップロックをセットにしたロック。
先頭のレコードより前、末尾のレコードより後
それぞれ、先頭レコードよりも前にある疑似レコードと、末尾レコードより後にある疑似レコードを意味する。「先頭レコードより前」や「末尾レコードより後」のギャップロックを表現するために使われる。
日本語の方はあまり使わないと思う。
先頭レコードより前への挿入をブロックするには、先頭レコードとinfimumレコードのギャップをロックする必要がある。
また、末尾レコードより後ろへの挿入をブロックするには、末尾レコードとsupremumレコードのギャップをロックする必要がある。
「先頭レコードのネクストキーロック」を使えば「先頭レコードとinfimumのギャップ」もロックされる。そのため、infimumは実際にはあまり出番がないようだ。
しかし、「末尾レコードのネクストキーロック」では「末尾レコードとsupremumレコードのギャップ」をロックすることはできない。そのため、innodb_lock_monitor
で見るとsupremum
の表示はよくある。
インテンションロック(intention lock)
テーブルに対するロックで、これにも2種類のモードがある。
- インテンション共有ロック(intention shared lock):
(IS)
と表記される - インテンション排他ロック(intention exclusive lock):
(IX)
と表記される
インテンションロックは、行ロックとテーブルロックを共存させるための実装上の都合で存在するロック。
動作としては、トランザクションTx1
がテーブルtest
上で行ロック(X)を取得しようとする場合、先にテーブルtest
に対するテーブルロック(IX)の取得を試みる。(同様に、行ロック(S)を取得しようとする場合ならば、テーブルロック(IS)を試みる。)
このとき、テーブルtest
が他のセッションですでにLOCK TABLES test READ;
で明示的にテーブルロック(S)されていたとする。すると、テーブルロック(S)とテーブルロック(IX)は競合するので、Tx1
はテーブルロック(IX)を取得できない。よって、Tx1
は行ロック(X)も取得できない。
これにより、テーブルロック済みのテーブル上のレコードに対する、競合する行ロックの取得を防げる。
別のケースとして、他のトランザクションTx2
がテーブルtest
上で行ロック(S)を取得していたとする。つまり、Tx2
はテーブルtest
に対するテーブルロック(IS)を取得済みということになる。この場合、テーブルロック(IS)とテーブルロック(IX)は競合しないので、Tx1
もテーブルロック(IX)を取得できる。
その後、Tx1
は、Tx2
が行ロック(S)していないレコードの範囲についてならば、行ロック(X)を取得できるだろう。
このように、行ロック時にもテーブルロックを使用して2段階にすることで、テーブルロックと行ロックを解りやすく共存できるようにしている。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.2.3 InnoDB のロックモードより、ロックの対応表を引用
X | IX | S | IS | |
---|---|---|---|---|
X | 競合 | 競合 | 競合 | 競合 |
IX | 競合 | 互換 | 競合 | 互換 |
S | 競合 | 競合 | 互換 | 互換 |
IS | 競合 | 互換 | 互換 | 互換 |
- インテンション同士は(共有・排他を問わず)競合しない
- 共有同士は(インテンションかどうかを問わず)競合しない
脚注
-
ドキュメントにはない表記。InnoDBのソース上の
LOCK_TABLE
定数とLOCK_REC
定数の定義してあるところにコメントで/** Lock types */
と書いてあったため、また、「ロックがLOCK_TABLE
かLOCK_REC
か返す関数」がlock_get_type_low
だったため、こう呼んでいる。 ↩ ↩2 -
ドキュメントにはない表記。InnoDBのソース上の
LOCK_GAP
やLOCK_REC_NOT_GAP
が定義してあるところにコメントでPrecise modes
と書いてあったため、こう呼んでいる。 ↩ ↩2