質問: 100; ``` > > id にインデックスが設定されていなかったり、一意でないインデックスが設定されていたりすると、このステートメントで**先行するギャップがロック**されます。 > > > 一方、後述の説明のInnoDB のさまざまな SQL ステートメントで設定されたロックでは、こういう文が書いてあります。 > > SELECT ... FROM は一貫性読み取りであり、データベースのスナップショットを読み取り、トランザクションの分離レベルが SERIALIZABLE に設定されなければ**ロックを設定しません**。 > > > 後者(ロック無し)と前者(インデックスレコードロック或いはギャップロックが必要)との表現には、一致しない部分があるようです。何かを間違えたと思いますけど、ご指導のほどよろしくお願いいたします
回答: MySQL のデフォルトトランザクションレベルである REPEATABLE READ では、その動作として、以下の挙動をベースとしています。 * 読み込み系は、特定時点の snapshot (テーブルの参照用コピーのようなもの)に対して実行される。最新のテーブル状態を見ているわけではないので、ロック云々は不要。 * 更新系は、今現在最新のテーブルに対して実行され、トランザクションが終了するまでは、そのトランザクションが行なった更新はその他トランザクションによって上書きされることがない このとき、特に更新系においてこの性質を持たせるために、 update や delete の where 句に対して、 MySQL は InnoDB が用意する諸々のロックを用います。何かというと、そのトランザクションが更新を行なったテーブルに対しては、そのトランザクションが終了するまではもろもろのロックをかけておいて、 REPEATABLE READ の更新系の性質を満たすようにする。 なので、質問の前半部分のネクストキーロックの話やギャップロックの話は、基本的には「ロックをかけるキースキャンを発行する場合」の話です。たとえば、 id が primary key (つまり unique index の一種) である場合には、以下の更新は、id = 100 のレコードが存在すれば、ギャップロックを取得しません。 ``` UPDATE table_name SET name = 'new_name' WHERE id = 100; ``` ではなぜこれらロックの話で 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 文とトランザクションレベル、また実際のデータがどうなっているかに依存している。質問のギャップロックの説明は、どのようにこのロックが決まるかのロジックの一部を説明している。