🔒

ギャップロックについて簡単に調べた

2024/10/21に公開

先日、業務でとあるインシデントが発生したのですが、その原因がギャップロックでした。
個人的にはそのタイミングでギャップロックを初めて知ったので、「ギャップロックとは何か」「どういうときに発生するのか」などについて簡単に調べてみたので、そのことについて個人的メモとしてまとめてみます。

ギャップロックとは

ギャップロックとは、レコードの間に対して発生するロックのことです。

なぜ発生するのか

ファントムリードを防ぐためにロックがかかります。

ファントムリードとは、あるトランザクションでデータを追加or削除した場合に、他のトランザクションで読み込んだ場合に異なるデータを読み込んでしまうことを指します。

どのような場合に発生するのか

実際にどのような現象なのか確認してみましょう。

DBの状態は以下の通りです。

mysql> select * from users;
+----+--------+
| id | name   |
+----+--------+
| 10 | Taro   |
| 20 | Jiro   |
| 30 | Saburo |
| 40 | Shiro  |
| 50 | Goro   |
+----+--------+
5 rows in set (0.01 sec)

トランザクション1(T1)

範囲指定してselect ~ for updateを実行してみます。

select * from users where id >= 15 and id <= 25 for update;

問題なく実行できます。

トランザクション2(T2)

T1で指定した範囲にレコードをインサートしようとしてみます。

insert users (id, name) values (23, 'hanako');

ここで待ちが発生します。これはT1で指定した15~25の間にギャップロックが発生しているからです。

select ~ for updateじゃなかったらどうなる?

先ほどはselect ~ for updateで指定しましたが、selectの場合はどうなるのでしょうか?

トランザクション1(T1)

以下のクエリを実行します。

select * from users where id >= 15 and id <= 25;

もちろん実行できます。

トランザクション2(T2)

次に以下のクエリを実行します。

insert users (id, name) values (23, 'hanako');

今度は実行できました。
とうやらギャップロックはselect ~ for updateでレコードを取得した場合に発生するみたいですね。

まとめ

以上が簡単なギャップロックの説明になります。

インデックスを張っているorいない、プライマリーキーorセカンダリーキーなども挙動に影響するっぽいのですが、今回は調べる余裕がなかったので、またの機会に調査してみようと思います。

Discussion