InnoDB
は2つのタイプのロックがあるスタンダード行レベル
ロックを実装します:
共有(S
)ロックはトランザクションが行を読む事を許容します(タプル)。
専用(X
)ロックはトランザクションが行を更新、削除する事を許容します。
もしトランザクション T1
が共有(S
)ロックをタプル
t
上で保持していたら、
t
上の S
ロックの為のいくつかの独特なトランザクション
T2
からのリクエストは直ちに認められます。結果として、T1
と T2
の両方は t
上で S
ロックを保持します。
t
上の X
ロックの為のいくつかの独特なトランザクション
T2
からのリクエストは直ちに認められます。
もしトランザクション T1
が独特な(X
)ロックをタプル
t
上で保持していたら、その時は
t
上のロックに対する独特なトランザクション
T2
からのリクエストは直ちに認められません。代わりに、トランザクション
T2
はトランザクション
T1
がタプル t
上でそのロックをリリースするのを待たなければいけません。
さらに、InnoDB
はレコード
ロックの共存とテーブル全体のロックを許容する
複数粒度ロック
をサポートします。複数粒度レベルでのロックを実用的にする為に、インテンション
ロック
と呼ばれるロックの追加タイプが利用されます。インテンション
ロックは InnoDB
内のテーブル
ロックです。インテンション
ロックの裏にある考えは、後でトランザクションが、そのテーブル内でどのタイプ(共有か専用か)のロックを行の為に要求するのかを指示するという事です。InnoDB
内で利用されるインテンション
ロックには2つのタイプがあります。(トランザクション
T
がテーブル R
上で指示されたタイプのロックをリクエストしたと仮定してください。):
共有インテンション(IS
):トランザクション
T
は R
内の独立行上にS
ロックを設定する予定です。
専用インテンション(IX
):トランザクション
T
はそれらの行上に
X
ロックを設定する予定です。
インテンション ロック プロトコルは次のようになります:
与えられたトランザクションは与えられた行上で
S
ロックを得る前に、まずその行を含んでいるテーブル上に
IS
か、またはさらに強いロックを得る必要があります。
与えられたトランザクションは与えられた行上で
X
ロックを得る前に、まずその行を含んでいるテーブル上に
IX
ロックを得る必要があります。
これらのルールは ロック タイプ変換互換性マトリックス を用いて便利に要約する事ができます:
X |
IX |
S |
IS |
|
X |
対立 | 対立 | 対立 | 対立 |
IX |
対立 | 互換性あり | 対立 | 互換性あり |
S |
対立 | 対立 | 互換性あり | 互換性あり |
IS |
対立 | 互換性あり | 互換性あり | 互換性あり |
既存ロックと互換性があれば、リクエストしているトランザクションにロックが与えられます。既存ロックと対立すれば、リクエストしているトランザクションにロックは与えられません。トランザクションは、既存の対立中のロックがリリースされるまで待ちます。もしロックのリクエストが既存ロックと対立する為にデッドロックが起り、そのロックが与えられないとしたら、エラーが発生します。
従って、インテンション ロックはフル
テーブル
リクエスト以外は何もブロックしません。(例えば
LOCK TABLES ...
WRITE
)IX
と
IS
ロックの主な目的は、誰かが行をロックしている、またはテーブル内の行をロックしようとしている、という事です。
次の例は、ロック リクエストがデッドロックを引き起こす時にどのようにエラーが発生するかを表しています。この例には、A と B の2つのクライアントが登場します。
最初に、クライアント A
が行を1つ含むテーブルを作成し、トランザクションを開始します。トランザクション内で、A
は共有モードで選択した行に
S
ロックを獲得します:
mysql>CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec) mysql>INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec) mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec) mysql>SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+ | i | +------+ | 1 | +------+ 1 row in set (0.10 sec)
次に、クライアント B がトランザクションを開始し、テーブルから行を削除しようとします:
mysql>START TRANSACTION;
Query OK, 0 rows affected (0.00 sec) mysql>DELETE FROM t WHERE i = 1;
削除作業は X
ロックを必要とします。クライアント A
が保持している S
ロックと互換性が無い為にそのロックは与えられず、そのリクエストは行とクライアント
B のロック リクエストの列に並びます。
最後に、クライアント A もテーブルから行を削除しようとします:
mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction
クライアント A は行を削除する為に
X
ロックが必要なので、ここでデッドロックが発生します。しかし、クライアント
B が既に X
ロックへのリクエストを持ち、またその
S
ロックをクライアント A
がリリースするのを待っている為に、ロック
リクエストは与えられません。B による
X
ロックのリクエストの為に、A
によって保持されている S
ロックをX
ロックにアップグレードするる事もできません。結果として、InnoDB
はクライアント A
に対してエラーを生成し、そのロックをリリースします。その時点で、クライアント
B のロック リクエストが与えられ、B
はテーブルから行を削除します。