LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
UNLOCK TABLES
MySQL では、クライアントセッションは、ほかのセッションと連携してテーブルにアクセスするために、またはそのセッションにテーブルへの排他的アクセスが必要な期間中はほかのセッションによってそのテーブルが変更されないようにするために、明示的にテーブルロックを取得できます。 セッションがロックを取得または解放できるのは、それ自体のためだけです。 あるセッションが別のセッションのためにロックを取得したり、別のセッションによって保持されているロックを解放したりすることはできません。
ロックを使用すると、トランザクションをエミュレートするか、またはテーブル更新時の速度を向上させることができます。 詳細は、テーブルロックの制限と条件 を参照してください。
LOCK TABLES
は、現在のクライアントセッションのテーブルロックを明示的に取得します。 テーブルロックは、ベーステーブルまたはビューに対して取得できます。 ロックされる各オブジェクトに対する LOCK TABLES
権限と SELECT
権限が必要です。
ビューのロックの場合、LOCK TABLES
は、そのビューで使用されているすべてのベーステーブルをロックされるテーブルのセットに追加し、それらのテーブルを自動的にロックします。 ロックされるビューの基礎となるテーブルの場合、LOCK TABLES
は、ビュー定義者 (SQL SECURITY DEFINER
ビューの場合) または実行者 (すべてのビューの場合) がテーブルに対する適切な権限を持っていることを確認します。
LOCK TABLES とトリガーで説明されているように、LOCK TABLES
によって明示的にテーブルをロックした場合は、トリガーで使用されているテーブルもすべて暗黙的にロックされます。
LOCK TABLES
を使用してテーブルを明示的にロックすると、外部キー制約に関連するテーブルが暗黙的にオープンおよびロックされます。 外部キーチェックでは、関連するテーブルに対して共有読取り専用ロック (LOCK TABLES READ
) が取得されます。 カスケード更新では、操作に関連する関連テーブルに対してシェアードナッシング書込みロック (LOCK TABLES WRITE
) が取得されます。
UNLOCK TABLES
は、現在のセッションによって保持されているテーブルロックをすべて明示的に解放します。 LOCK TABLES
は、新しいロックを取得する前に、現在のセッションによって保持されているテーブルロックをすべて暗黙的に解放します。
UNLOCK TABLES
の別の使用法として、すべてのデータベース内のすべてのテーブルをロックできる FLUSH TABLES WITH READ LOCK
ステートメントによって取得されたグローバルな読み取りロックの解放があります。 セクション13.7.8.3「FLUSH ステートメント」を参照してください。 (これは、特定時点のスナップショットを取得できる、Veritas などのファイルシステムがある場合にバックアップを取得するための非常に便利な方法です。)
テーブルロックは、ほかのセッションによる不適切な読み取りまたは書き込みからのみ保護します。 WRITE
ロックを保持しているセッションは、DROP TABLE
や TRUNCATE TABLE
などのテーブルレベルの操作を実行できます。 READ
ロックを保持しているセッションの場合、DROP TABLE
および TRUNCATE TABLE
操作は許可されません。
次の説明は、TEMPORARY
以外のテーブルにのみ適用されます。 LOCK TABLES
は TEMPORARY
テーブルに対して許可されます (ただし、無視されます)。 テーブルは、ほかのどのようなロックが有効になっているかには関係なく、そのテーブルが作成されたセッションから自由にアクセスできます。 ほかのどのセッションもそのテーブルを参照できないため、ロックは必要ありません。
現在のセッション内でテーブルロックを取得するには、メタデータロックを取得する LOCK TABLES
ステートメントを使用します (セクション8.11.4「メタデータのロック」 を参照)。
次のロックタイプを使用できます。
READ [LOCAL]
ロック:
このロックを保持しているセッションは、テーブルを読み取ることができます (ただし、書き込みはできません)。
複数のセッションが同時にテーブルに対する
READ
ロックを取得できます。ほかのセッションは、
READ
ロックを明示的に取得することなく、テーブルを読み取ることができます。LOCAL
修飾子を使用すると、ロックが保持されている間、ほかのセッションによる競合しないINSERT
ステートメント (並列挿入) を実行できます。 (セクション8.11.3「同時挿入」を参照してください。) ただし、ロックを保持している間、サーバーの外部にあるプロセスを使用してデータベースを操作しようとしている場合は、READ LOCAL
を使用できません。InnoDB
テーブルの場合、READ LOCAL
はREAD
と同じです。
[LOW_PRIORITY] WRITE
ロック:
このロックを保持しているセッションは、テーブルの読み取りおよび書き込みが可能です。
このロックを保持しているセッションだけがテーブルにアクセスできます。 ロックが解放されるまで、ほかのどのセッションもアクセスできません。
WRITE
ロックが保持されている間、テーブルに対するほかのセッションからのロック要求はブロックされます。LOW_PRIORITY
修飾子は何の効果もありません。 以前のバージョンの MySQL では、ロックの動作に影響を与えましたが、これは当てはまらなくなっています。 これは非推奨になり、使用すると警告が生成されます。 代わりに、LOW_PRIORITY
のないWRITE
を使用してください。
WRITE
ロックは通常、更新ができるだけ早く処理されるように、READ
ロックより高い優先度を持っています。 つまり、あるセッションが READ
ロックを取得したあと、別のセッションが WRITE
ロックを要求した場合は、WRITE
ロックを要求したセッションがロックを取得して解放するまで、以降の READ
ロック要求が待たされます。 (このポリシーの例外は、max_write_lock_count
システム変数の小さい値に対して発生する可能性があります。セクション8.11.4「メタデータのロック」 を参照してください。)
LOCK TABLES
ステートメントが、いずれかのテーブルに対するほかのセッションによって保持されているロックのために待機する必要がある場合、このステートメントはすべてのロックを取得できるまでブロックされます。
ロックが必要なセッションは、必要なすべてのロックを 1 つの LOCK TABLES
ステートメントで取得する必要があります。 このように取得されたロックが保持されている間、このセッションは、ロックされたテーブルにのみアクセスできます。 たとえば、次のステートメントシーケンスでは、t2
が LOCK TABLES
ステートメントでロックされていないため、このテーブルにアクセスしようとするとエラーが発生します。
mysql> LOCK TABLES t1 READ;
mysql> SELECT COUNT(*) FROM t1;
+----------+
| COUNT(*) |
+----------+
| 3 |
+----------+
mysql> SELECT COUNT(*) FROM t2;
ERROR 1100 (HY000): Table 't2' was not locked with LOCK TABLES
INFORMATION_SCHEMA
データベース内のテーブルは例外です。 これらのテーブルは、セッションが LOCK TABLES
によって取得されたテーブルロックを保持している間であっても、明示的にロックされることなくアクセスできます。
ロックされたテーブルを、同じ名前を使用して 1 つのクエリーで複数回参照することはできません。 代わりにエイリアスを使用し、そのテーブルと各エイリアスのための個別のロックを取得します。
mysql> LOCK TABLE t WRITE, t AS t1 READ;
mysql> INSERT INTO t SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> INSERT INTO t SELECT * FROM t AS t1;
最初の INSERT
では、ロックされたテーブルに対する同じ名前への参照が 2 つ存在するため、エラーが発生します。 2 番目の INSERT
は、テーブルへの参照で異なる名前が使用されるため、成功します。
ステートメントがエイリアスを使用してテーブルを参照する場合は、その同じエイリアスを使用してテーブルをロックする必要があります。 エイリアスを指定しないでテーブルをロックすることはできません。
mysql> LOCK TABLE t READ;
mysql> SELECT * FROM t AS myalias;
ERROR 1100: Table 'myalias' was not locked with LOCK TABLES
逆に、エイリアスを使用してテーブルをロックする場合は、ステートメント内でそのエイリアスを使用してテーブルを参照する必要があります。
mysql> LOCK TABLE t AS myalias READ;
mysql> SELECT * FROM t;
ERROR 1100: Table 't' was not locked with LOCK TABLES
mysql> SELECT * FROM t AS myalias;
セッションによって保持されているテーブルロックが解放される場合は、すべてのテーブルロックが一度に解放されます。 セッションは明示的にロックを解放できます。また、特定の状況で、ロックが暗黙的に解放される場合もあります。
セッションは、
UNLOCK TABLES
によって明示的にロックを解放できます。セッションがすでにロックを保持している間にロックを取得するために
LOCK TABLES
ステートメントを発行した場合は、新しいロックが付与される前に、その既存のロックが暗黙的に解放されます。セッションが (たとえば、
START TRANSACTION
で) トランザクションを開始した場合は、暗黙的なUNLOCK TABLES
が実行され、既存のロックが解放されます。 (テーブルロックとトランザクションの間の通信の詳細は、テーブルロックとトランザクションの通信を参照してください。)
クライアントセッションの接続が (正常または異常にかかわらず) 終了した場合、サーバーは、そのセッションによって保持されているすべてのテーブルロック (トランザクションおよび非トランザクション) を暗黙的に解放します。 クライアントが再接続すると、ロックは無効になります。 さらに、クライアントにアクティブなトランザクションがある場合、サーバーは切断時にそのトランザクションをロールバックし、再接続が発生した場合は、自動コミットが有効になった状態で新しいセッションが開始されます。 このため、クライアントは自動再接続を無効にすることが必要になる場合があります。 自動再接続が有効な場合、再接続が発生してもテーブルロックまたは現在のトランザクションが失われても、クライアントには通知されません。 自動再接続が無効になっている場合は、接続が削除されると、発行された次のステートメントに対してエラーが発生します。 クライアントはそのエラーを検出し、ロックの再取得やトランザクションの再実行などの適切なアクションを実行できます。 Automatic Reconnection Controlを参照してください。
ロックされたテーブル上で ALTER TABLE
を使用すると、そのテーブルがロック解除される場合があります。 たとえば、2 番目の ALTER TABLE
操作を試みると、エラー「テーブル '
が発生する場合があります。 これに対処するには、2 番目の変更の前にテーブルを再度ロックします。 セクションB.3.6.1「ALTER TABLE での問題」も参照してください。
tbl_name
' は LOCK TABLES でロックされていません」
LOCK TABLES
および UNLOCK TABLES
は、トランザクションの使用との間で次のように通信します。
LOCK TABLES
はトランザクションセーフではないため、テーブルをロックしようとする前に、アクティブなトランザクションをすべて暗黙的にコミットします。-
UNLOCK TABLES
は、アクティブなトランザクションをすべて暗黙的にコミットしますが、これが行われるのは、テーブルロックを取得するためにLOCK TABLES
が使用された場合のみです。 たとえば、次の一連のステートメントでは、UNLOCK TABLES
がグローバルな読み取りロックを解放しますが、有効なテーブルロックがないためにトランザクションはコミットされません。FLUSH TABLES WITH READ LOCK; START TRANSACTION; SELECT ... ; UNLOCK TABLES;
トランザクションを (たとえば、
START TRANSACTION
で) 開始すると、現在のトランザクションはすべて暗黙的にコミットされ、既存のテーブルロックが解放されます。FLUSH TABLES WITH READ LOCK
は、グローバルな読み取りロックを取得しますが、テーブルロックは取得しないため、テーブルロックと暗黙的なコミットに関してLOCK TABLES
およびUNLOCK TABLES
と同じ動作には従いません。 たとえば、START TRANSACTION
は、グローバルな読み取りロックを解放しません。 セクション13.7.8.3「FLUSH ステートメント」を参照してください。暗黙的にトランザクションのコミットを発生させるその他のステートメントは、既存のテーブルロックを解放しません。 このようなステートメントのリストについては、セクション13.3.3「暗黙的なコミットを発生させるステートメント」を参照してください。
-
トランザクションテーブル (
InnoDB
テーブルなど) でLOCK TABLES
およびUNLOCK TABLES
を使用するための正しい方法は、SET autocommit = 0
(START TRANSACTION
ではなく) に続けてLOCK TABLES
を指定することによってトランザクションを開始し、そのトランザクションを明示的にコミットするまでUNLOCK TABLES
を呼び出さないことです。 たとえば、テーブルt1
に書き込み、テーブルt2
から読み取る必要がある場合は、次のように実行できます。SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...; ... do something with tables t1 and t2 here ... COMMIT; UNLOCK TABLES;
LOCK TABLES
を呼び出すと、InnoDB
は内部的に独自のテーブルロックを取得し、MySQL は独自のテーブルロックを取得します。InnoDB
は次のコミット時に内部のテーブルロックを解放しますが、MySQL でテーブルロックが解放されるようにするには、UNLOCK TABLES
を呼び出す必要があります。autocommit = 1
を指定すると、LOCK TABLES
の呼び出しの直後にInnoDB
によって内部のテーブルロックが解放され、デッドロックが非常に発生しやすくなる場合があるため、この指定は行わないようにしてください。autocommit = 1
が指定された場合、古いアプリケーションが不必要なデッドロックを回避するのに役立つように、InnoDB
は内部のテーブルロックをまったく取得しません。 ROLLBACK
は、テーブルロックを解放しません。
LOCK TABLES
によって明示的にテーブルをロックした場合は、トリガーで使用されているテーブルもすべて暗黙的にロックされます。
これらのロックは、
LOCK TABLES
ステートメントによって明示的に取得されるロックと同時に取得されます。トリガーで使用されているテーブルに対するロックは、そのテーブルが読み取りのみに使用されているかどうかによって異なります。 読み取りのみに使用されている場合は、読み取りロックで十分です。 そうでない場合は、書き込みロックが使用されます。
テーブルが
LOCK TABLES
によって読み取りに対して明示的にロックされているが、トリガー内で変更される可能性があるために書き込みに対してロックする必要がある場合は、読み取りロックではなく書き込みロックが取得されます。 (つまり、トリガー内でのテーブルの表示のために必要な暗黙の書き込みロックによって、テーブルに対する明示的な読み取りロック要求が書き込みロック要求に変換されます。)
次のステートメントを使用して、2 つのテーブル t1
と t2
をロックするとします。
LOCK TABLES t1 WRITE, t2 READ;
t1
または t2
にトリガーがある場合、トリガー内で使用されるテーブルもロックされます。 t1
に、次のように定義されたトリガーが含まれているとします。
CREATE TRIGGER t1_a_ins AFTER INSERT ON t1 FOR EACH ROW
BEGIN
UPDATE t4 SET count = count+1
WHERE id = NEW.id AND EXISTS (SELECT a FROM t3);
INSERT INTO t2 VALUES(1, 2);
END;
LOCK TABLES
ステートメントの結果として、t1
と t2
は、このステートメントに現れるためにロックされます。また、t3
と t4
は、トリガー内で使用されているためにロックされます。
t1
は、WRITE
ロック要求ごとに、書き込みに対してロックされます。t2
は、要求がREAD
ロックに対するものであったとしても、書き込みに対してロックされます。 これは、トリガー内でt2
に挿入されるために発生します。したがって、READ
要求はWRITE
要求に変換されます。t3
は、トリガー内から読み取られるだけであるため、読み取りに対してロックされます。t4
は、トリガー内で更新される可能性があるため、書き込みに対してロックされます。
テーブルロックを待機しているセッションを終了するために、KILL
を安全に使用できます。 セクション13.7.8.4「KILL ステートメント」を参照してください。
LOCK TABLES
および UNLOCK TABLES
は、ストアドプログラム内では使用できません。
performance_schema
データベース内のテーブルは、setup_
テーブルを除き、xxx
LOCK TABLES
ではロックできません。
LOCK TABLES
ステートメントが有効になっている間、次のステートメントは禁止されます。CREATE TABLE
、CREATE TABLE ... LIKE
、CREATE VIEW
、DROP VIEW
、およびストアドファンクション、ストアドプロシージャー、イベントでの DDL ステートメント。
一部の操作では、mysql
データベース内のシステムテーブルにアクセスする必要があります。 たとえば、HELP
ステートメントにはサーバー側のヘルプテーブルの内容が必要であり、また CONVERT_TZ()
はタイムゾーンテーブルの読み取りが必要になる可能性があります。 サーバーは、ユーザーが明示的にロックしなくても済むように、必要に応じてシステムテーブルを読み取りに対して暗黙的にロックします。 次のテーブルは、今説明したように処理されます。
mysql.help_category
mysql.help_keyword
mysql.help_relation
mysql.help_topic
mysql.time_zone
mysql.time_zone_leap_second
mysql.time_zone_name
mysql.time_zone_transition
mysql.time_zone_transition_type
これらのテーブルのいずれかに対する WRITE
ロックを LOCK TABLES
ステートメントで明示的に設定する場合は、そのテーブルがロックされる唯一のテーブルである必要があります。ほかのどのテーブルも、同じステートメントではロックできません。
1 つの UPDATE
ステートメントはすべてアトミックであるため、通常、テーブルをロックする必要はありません。現在実行中の SQL ステートメントを、ほかのどのセッションも妨げることはできません。 ただし、テーブルのロックによって利点が得られる可能性のある場合がいくつかあります。
-
一連の
MyISAM
テーブルに対して多くの操作を実行しようとしている場合は、使用しようとしているテーブルをロックする方がはるかに高速です。MyISAM
テーブルをロックすると、MySQL はロックされたテーブルのキーキャッシュをUNLOCK TABLES
が呼び出されるまでフラッシュしないため、そのテーブルに対する挿入、更新、または削除が高速化されます。 通常、キーキャッシュは各 SQL ステートメントのあとでフラッシュされます。テーブルロックのマイナス面は、
READ
によってロックされたテーブルをどのセッションも更新できず (ロックを保持しているセッションを含む)、ロックを保持しているセッションを除き、WRITE
によってロックされたテーブルにどのセッションもアクセスできない点です。 -
非トランザクションストレージエンジンに対してテーブルを使用している場合、
SELECT
とUPDATE
の間にテーブルがほかのセッションによって変更されないようにするには、LOCK TABLES
を使用する必要があります。 次に示す例では、安全に実行するためにLOCK TABLES
が必要です。LOCK TABLES trans READ, customer WRITE; SELECT SUM(value) FROM trans WHERE customer_id=some_id; UPDATE customer SET total_value=sum_from_previous_statement WHERE customer_id=some_id; UNLOCK TABLES;
LOCK TABLES
を使用しない場合は、SELECT
ステートメントとUPDATE
ステートメントの実行の間に、別のセッションによってtrans
テーブルに新しい行が挿入される可能性があります。
多くの場合は、相対的な更新 (UPDATE customer SET
) または value
=value
+new_value
LAST_INSERT_ID()
関数を使用することによって LOCK TABLES
の使用を回避できます。
場合によっては、ユーザーレベルのアドバイザリロック関数 GET_LOCK()
および RELEASE_LOCK()
を使用してテーブルのロックを回避することもできます。 高速化のために、これらのロックはサーバーのハッシュテーブル内に保存され、pthread_mutex_lock()
と pthread_mutex_unlock()
で実装されます。 セクション12.15「ロック関数」を参照してください。
ロックポリシーの詳細は、セクション8.11.1「内部ロック方法」を参照してください。