

MySQLのリファレンスマニュアルでは、ギャップロック[[1]](https://dev.mysql.com/doc/refman/5.6/ja/innodb-
record-level-locks.html)について、こういう説明と例が取り上げられています。

> 一意のインデックスを使用して一意の行を検索することで行をロックするステートメントでは、ギャップロックは必要ありません。...たとえば、id
> カラムに一意のインデックスが設定されている場合、次のステートメントで使用されるのは id の値が 100 の行に対する
> **インデックスレコードロックだけ** となり、ほかのセッションがそのレコードの前にあるギャップに行を挿入するかどうかは問題ではなくなります。
[code]

    SELECT * FROM child WHERE id = 100;
    
[/code]

> id にインデックスが設定されていなかったり、一意でないインデックスが設定されていたりすると、このステートメントで **先行するギャップがロック**
> されます。

一方、後述の説明の[InnoDB のさまざまな SQL
ステートメントで設定されたロック](https://dev.mysql.com/doc/refman/5.6/ja/innodb-locks-
set.html)では、こういう文が書いてあります。

> SELECT ... FROM は一貫性読み取りであり、データベースのスナップショットを読み取り、トランザクションの分離レベルが
> SERIALIZABLE に設定されなければ **ロックを設定しません** 。

後者（ロック無し）と前者（インデックスレコードロック或いはギャップロックが必要）との表現には、一致しない部分があるようです。何かを間違えたと思いますけど、ご指導のほどよろしくお願いいたします


CC BY-SA 4.0
2018-10-31T09:43:49.390
49822
2018-11-01T04:50:03.583
2018-10-31T09:49:00.537
30643
30643
question
mysql database
一意のインデックスを使用したSELECT ... WHERE id = 100というステートメントでは、ロックする必要がありますか


MySQL のデフォルトトランザクションレベルである REPEATABLE READ では、その動作として、以下の挙動をベースとしています。

  * 読み込み系は、特定時点の snapshot (テーブルの参照用コピーのようなもの)に対して実行される。最新のテーブル状態を見ているわけではないので、ロック云々は不要。
  * 更新系は、今現在最新のテーブルに対して実行され、トランザクションが終了するまでは、そのトランザクションが行なった更新はその他トランザクションによって上書きされることがない

このとき、特に更新系においてこの性質を持たせるために、 update や delete の where 句に対して、 MySQL は InnoDB
が用意する諸々のロックを用います。何かというと、そのトランザクションが更新を行なったテーブルに対しては、そのトランザクションが終了するまではもろもろのロックをかけておいて、
REPEATABLE READ の更新系の性質を満たすようにする。

なので、質問の前半部分のネクストキーロックの話やギャップロックの話は、基本的には「ロックをかけるキースキャンを発行する場合」の話です。たとえば、 id が
primary key (つまり unique index の一種) である場合には、以下の更新は、id = 100
のレコードが存在すれば、ギャップロックを取得しません。

[code]

    UPDATE table_name SET name = 'new_name' WHERE id = 100;
    
[/code]

ではなぜこれらロックの話で SELECT 文が具体例として用いられているかというと、以下の場合において SELECT
文でも更新系と同じようなロックを取得することができるからです。

  * `SELECT ... FROM ... FOR UPDATE` 構文 
    * `SELECT ... FROM ... FOR UPDATE` の読み取りロック版である `SELECT ... FROM ... LOCK IN SHARE MODE` 構文
  * SERIALIZABLE トランザクションレベルにおける単純 `SELECT ... FROM ...` 構文. 
    * `SELECT ... FROM ... LOCK IN SHARE MODE` に勝手に変換される

まとめると:

  * ギャップロックを取得するケースや取得しないケースについての説明は、「ロックを実行するスキャンを実行したとき」の話である
  * SERIALIZABLE だと単純 SELECT 文は勝手に `LOCK IN SHARE MODE` され、なのでロックをかけないスキャンというものは実行されない
  * REPEATABLE READ 以下のトランザクションレベルで動作している場合、ロックが取得されるかどうかは実行している SQL 文の種類とトランザクションレベルに依存し、またどのようなロックが取得されるかは、 SQL 文とトランザクションレベル、また実際のデータがどうなっているかに依存している。質問のギャップロックの説明は、どのようにこのロックが決まるかのロジックの一部を説明している。


CC BY-SA 4.0
2018-11-01T04:36:00.913
49840
2018-11-01T04:50:03.583
2018-11-01T04:50:03.583
754
754
49822
answer