MySQL InnoDBの行レベルロック

InnoDBの利用

この機能はMyISAMにはなかったものなので、自分用のメモを書きました。


行レベル・ロックとは

行レベル・ロックは「レコード単位のロック機能」のことです。1レコードだけロックされるわけではなく、対象となる複数レコードがロックされます。

innoDBテーブル・タイプでは、レコード更新時とSELECT文(ロック・オプション付き)で行レベル・ロックが行われます。


読み取り時のロック

通常の「SELECT .. FROM ..」というステートメントでは、いっさいロックされません。また、読み取り一貫性機能によって、こういったクエリーを実行した後に、他でロックしても読み取りを続行することは可能です。

読み取り時にロックしたい場合、明示的にロック方法を指定する必要があります。

ロック方法 SQL
共有モード SELECT .. FROM .. LOCK IN SHARE MODE
排他モード SELECT .. FROM .. FOR UPDATE

共有モードでは、まだ残っている更新トランザクションが存在したら、まず、そのトランザクションが終了するまで待機します。そして、すべての更新が完了した後、データを読み出します。ですから、「できうる限り最新のデータ」を取得することになります。
そして、共有モードのロックが解除されるまで、そのレコードへの更新操作はできなくなります(読み取りは可能です)。

排他モードでは、まだ残っている更新トランザクションが存在したら、まず、そのトランザクションが終了するまで待機します。そして、すべての更新が完了した後、データを読み出します。つまり、排他モードでも「できうる限り最新のデータ」を取得することになります。
排他モードのロックは、そのレコードを他から書き込みすることも読み取りすることもできなくなります。

なお、データを更新するSQLは排他モードでのロックとなります。


MySQL独自のロック機構

MySQLの行レベル・ロックは、独特なルールがあります。たとえば、次のSQLを実行した場合を考えてみます。

  1. START TRANSACTION
  2. SELECT MAX(インデックス) AS 最大インデックス
      FROM テストテーブル LOCK IN SHARE MODE
  3. INSERT INTO テストテーブル (インデックス) VALUES (101)
  4. COMMIT

この場合、最初のSQLでは「最大インデックス」を求めています。仮にこの値が「100」だとします。 すると、MySQLでは他のクライアントが「インデックス=101」や「インデックス=200」というレコードを追加することが不可能となります。

実際には「インデックス=101」というレコードはMAX関数を利用しているので存在しません。
実際にレコードが存在しなくてもロックされるのです。

このようにして、MySQLでは実際にはレコードがなくてもロック動作が行われます。

このロック方法がMySQLのデフォルト動作ですが、これでは不都合な場合、「SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL」文を実行してロック動作を変更することができます。

Copyright©2001-2019 釣ったよ! All Right Reserved.    sg@tsuttayo.jpn.org