「予測① 書き込みは、どの分離レベルでも同様に排他ロックを取得する」の検証手順と、詳細な予測。
検証手順
各トランザクション分離レベルで、トランザクション内の書き込み操作が排他ロックを取得しているかを確認する。
- ターミナルを開き、
SET SESSION TRANSACTION ISOLATION LEVEL
を設定する - トランザクション
Tx1
を開始する -
Tx1
で書き込みSQLを実行する -
innodb_lock_monitor
を確認する-
Tx1
が排他ロックを取得しているか? - その範囲は?
-
-
Tx1
をROLLBACK
する - 1~5の繰り返し
CREATE DATABASE TIL;
USE TIL;
CREATE TABLE test (
id INTEGER NOT NULL DEFAULT 0,
a INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
INSERT INTO test (id, a) VALUES (2, 20);
INSERT INTO test (id, a) VALUES (3, 30);
INSERT INTO test (id, a) VALUES (6, 30);
INSERT INTO test (id, a) VALUES (7, 10);
上記SQLでテーブルtest.TIL
に以下の4行が準備される。
id | a |
---|---|
2 | 20 |
3 | 30 |
6 | 30 |
7 | 10 |
/* 有効化 */
CREATE TABLE innodb_lock_monitor (a INT) ENGINE=INNODB;
/* 無効化 */
DROP TABLE innodb_lock_monitor;
※innodb_lock_monitor
の出力はかなりの量になるので、途中でMySQLをサーバーを停止してmysql_error.log
ファイルをリネームする等、適宜対応する。
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SHOW VARIABLES LIKE 'tx_isolation';
検証に使用するSQLと予測
INSERT
INSERT INTO test (id, a) VALUES (1, 10);
【予測①-INSERT】
どの分離レベルにおいても挿入した行のレコードロック(X)を取得する。
UPDATE
UPDATE test SET a = 200 WHERE id = 2;
UPDATE test SET a = 200 WHERE id = 2 OR id = 6;
UPDATE test SET a = 200 WHERE id = 2 OR id = 5;
UPDATE test SET a = 200 WHERE id = 2 OR id = 9;
-
UPDATE_1
やUPDATE_2
だけを見れば、実際に更新する行のレコードロック(X)で十分である -
UPDATE_3
やUPDATE_4
のような場合、存在しないレコードはロックできない- 他のトランザクションによって、条件に該当するレコードが挿入される可能性がある(そしてその行は更新されない)
- 少なくともSERIALIZABLEはこれを許可しない
-
id >= (条件の値)
となる最初のレコードのネクストキーロック(X)を取得すれば挿入を防げる
【予測①-UPDATE_1-4】
SERIALIZABLEは実際に更新するレコードのネクストキーロック(X)と、id >= (条件の値)
となる最初のレコードのネクストキーロック(X)を取得する。他の分離レベルもおそらく同じ。
弊害としてWHERE句の条件に該当しないのに挿入できなくなるレコードがある。
-
UPDATE_3
のロックによってid = 4
のレコードも挿入できなくなる -
UPDATE_4
のロックによってid = 8
およびid > 9
のレコードも挿入できなくなる
UPDATE test SET a = 200 WHERE id <= 2;
UPDATE test SET a = 200 WHERE id <= 4;
UPDATE test SET a = 200 WHERE id <= 9;
-
UPDATE_5
だけを見れば、実際に更新する行のネクストキーロック(X)で十分である -
UPDATE_6
やUPDATE_7
のような場合、存在しないレコードはロックできない- 他のトランザクションによって、条件に該当するレコードが挿入される可能性がある(そしてその行は更新されない)
- 少なくともSERIALIZABLEはこれを許可しない
-
id >= (条件の値)
となる最初のレコードのネクストキーロック(X)を取得すれば挿入を防げる
【予測①-UPDATE_5-7】
SERIALIZABLEは実際に更新するレコードのネクストキーロック(X)と、id >= (条件の値)
となる最初のレコードのネクストキーロック(X)を取得する。他の分離レベルもおそらく同じ。
弊害としてWHERE句の条件に該当しないのに挿入できなくなるレコードがある。
-
UPDATE_6
のロックによってid = 5
のレコードも挿入できなくなる -
UPDATE_7
のロックによってid > 9
のレコードも挿入できなくなる
UPDATE test SET a = 200 WHERE id >= 2;
UPDATE test SET a = 200 WHERE id >= 5;
UPDATE test SET a = 200 WHERE id >= 9;
- 実際に更新する行のネクストキーロック(X)だけだと
- 他のトランザクションによって、条件に該当するレコードが挿入される可能性がある(そしてその行は更新されない)
- 少なくともSERIALIZABLEはこれを許可しない
-
supremum
のネクストキーロック(X)を取得すれば挿入を防げる
【予測①-UPDATE_8-10】
SERIALIZABLEは実際に更新するレコードのネクストキーロック(X)と、supremum
のネクストキーロック(X)を取得する。他の分離レベルもおそらく同じ。
弊害としてWHERE句の条件に該当しないのに挿入できなくなるレコードがある。
-
UPDATE_9
のロックによってid = 4
のレコードも挿入できなくなる -
UPDATE_10
のロックによってid = 8
のレコードも挿入できなくなる
UPDATE test SET a = 300 WHERE a = 30;
UPDATE test SET a = 300 WHERE a = 100;
- 検索条件である
a
にインデックスがないので、全レコードをスキャンすることになる - レコードロック(X)だけだと
- 他のトランザクションによって、条件に該当するレコードが挿入される可能性がある(そしてその行は更新されない)
- 少なくともSERIALIZABLEはこれを許可しない
- ネクストキーロック(X)を取得すれば挿入を防げる
- レコードすべてをロックする?もしくは、実際に更新するレコードだけをロックする?
- 実際に更新する行のロックだけだと他のトランザクションによって、条件に該当しなかったレコードが更新され、条件に該当するようになる可能性がある(そしてその行は更新されない)
- 少なくともSERIALIZABLEはこれを許可しない
- スキャンしたレコードすべてのロックを取得すれば防げる
【予測①-UPDATE_11-12】
SERIALIZABLEは全行のネクストキーロック(X)とsupremum
のネクストキーロック(X)を取得する。他の分離レベルもおそらく同じ。
弊害としてWHERE句の条件に該当しなくも、すべてのレコードが挿入および更新できなくなる。
DELETE
DELETE FROM test WHERE id = 2;
DELETE FROM test WHERE id = 2 OR id = 6;
DELETE FROM test WHERE id = 2 OR id = 5;
DELETE FROM test WHERE id = 2 OR id = 9;
DELETE FROM test WHERE id <= 2;
DELETE FROM test WHERE id <= 4;
DELETE FROM test WHERE id <= 9;
DELETE FROM test WHERE id >= 2;
DELETE FROM test WHERE id >= 5;
DELETE FROM test WHERE id >= 9;
DELETE FROM test WHERE a = 30;
DELETE FROM test WHERE a = 100;
【予測①-DELETE】
UPDATE
の対応する番号と同じ。