LOCK TABLES
tbl_name [AS alias]
{READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias]
{READ [LOCAL] | [LOW_PRIORITY] WRITE}] ...
UNLOCK TABLES
LOCK TABLES は、現在のスレッドに対してベース テーブル (ビュー以外) をロックします。もしテーブルが1つでも他のスレッドによってロックされていたら、それは全てのロックを入手するまでブロックします。
UNLOCK TABLES は現在のスレッドによって行われたロックを全て明示的にリリースします。スレッドが別の LOCK TABLES を発行した時、またはサーバへの接続が閉じられた時に、現在のスレッドにロックされている全てのテーブルは暗黙的にロック解除されます。 UNLOCK TABLES はまた、FLUSH TABLES WITH READ LOCK を利用してグローバル リード ロックを取得した後に、そのロックをリリースする為に利用されます。(FLUSH TABLES WITH READ LOCK ステートメントによってリード ロックされている全てのデータベース内の全てのテーブルをロックする事ができます。詳しくは 項2. 「FLUSH 構文」 を参照してください。これは、もし Veritas のような時間内にスナップショットを撮る事ができるファイルシステムを持っていると、バックアップを取るのが大変便利な方法になります。)
LOCK TABLES を利用する為には、関連するテーブルに対して LOCK TABLES 権限と SELECT 権限を持つ必要があります。
LOCK TABLES を利用する主な理由は、テーブルを更新する際にトランザクションをエミュレートしたり、スピードを早くしたりする事です。後ほどもう少し詳しく説明します。
テーブル ロックは、別のクライアントによる不適切な読み込みや書き込みに対してのみ保護します。ロックを保持しているクライアントは、それがリード
ロックだとしても、DROP TABLE のようなテーブル レベル操作を行う事ができます。切り捨て操作はトランザクション セーフではないので、もし、アクティブなトランザクションの実行中や、テーブル
ロックを保持している最中にクライアントがそれを行おうとすると、エラーが発生します。
LOCK TABLES とトランザクション テーブルを共に利用する際には、次の事に注意してください。
LOCK TABLES はトランザクション セーフではないので、テーブルをロックしようとする前に、全てのアクティブなトランザクションを暗黙的にコミットします。また、トランザクションの開始をすると(例えば
START TRANSACTION を利用して)、明示的に UNLOCK TABLES を実行します。(詳しくは 項 「暗黙のコミットを引き起こすステートメント」 を参照してください。)
LOCK TABLES を InnoDB のようなトランザクション テーブルと共に利用する正しい方法は、AUTOCOMMIT = 0 を設定し、トランザクションを明示的にコミットするまでは UNLOCK TABLES をコールしないという方法です。LOCK TABLES をコールする時、InnoDB は内部的にそれ自身のテーブル ロックを取り、そして MySQL もそれ自身のテーブル ロックを取ります。InnoDB は次のコミットでテーブル ロックを解除しますが、MySQL がそのテーブル ロックを解除するには、 UNLOCK TABLES をコールする必要があります。InnoDB は LOCK TABLES のコールの後そのテーブル ロックを即座にリリースし、その為にデッドロックが簡単に起きてしまうので、AUTOCOMMIT = 1 を持つべきでは有りません。もし AUTOCOMMIT=1 であれば、古いアプリケーションの不必要なデッドロックを防ぐ為に、InnoDB テーブル ロックを全く取得しないという事に注意してください。
ROLLBACK は MySQL の非トランザクション テーブル ロックを解除しません。
FLUSH TABLES WITH READ LOCK は、グローバル リード ロックは取得しますがテーブル ロックはしないので、テーブル ロックと暗黙的なコミットに関しては LOCK TABLES と UNLOCK TABLES と同じような動作の制約は受けません。詳しくは 項2. 「FLUSH 構文」 を参照してください。
LOCK TABLES を利用する時、ステートメントの中で利用する予定の全てのテーブルをロックしなければいけません。LOCK TABLES はビューをロックしないので、もし今実行している操作がビューを利用するなら、それらのビューが依存する全てのベース テーブルもロックしなければいけません。LOCK TABLES ステートメントで取得したロックが有効な間は、そのステートメントによってロックされていないテーブルにアクセスする事はできません。また、単一クエリの中でロックされたテーブルを複数回使用する事はできません。各エイリアスに対して別々にロックを取得する必要がある場合には、代わりにエイリアスを利用してください。
mysql>LOCK TABLE t WRITE, t AS t1 WRITE;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;
もしステートメントがエイリアスを利用してテーブルを参照するなら、同じエイリアスを利用してテーブルをロックする必要があります。エイリアスを指定しないでテーブルをロックする事はできません。
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;
もしスレッドがテーブル上で READ ロックを取得したら、そのスレッド(そしてそれ以外の全てのスレッド)はテーブルからは読み込む事しかできません。もしスレッドがテーブル上で WRITE ロックを取得したら、そのロックを保持するスレッドのみがそのテーブルに書き込みができます。ロックが解除されるまで、その他のスレッドはテーブルの読み込み、書き込みからブロックされます。
READ LOCAL と READ の違いは、READ LOCAL は、ロックされている間に非対立 INSERT ステートメント(並列挿入)が実行される事を許容するという事です。しかし、もしロックを保持している間に MySQL の外部でデータベース ファイルを複製しようとすると、これを利用する事はできません。
InnoDB テーブルに対しては、READ LOCAL は READ と同じです。
WRITE ロックは通常、更新がなるべく速く行われるよう、READ ロックよりも高い優先順位を持ちます。これは、もし1つのスレッドが READ ロックを取得し、そして別のスレッドが WRITE ロックをリクエストすると、後続の READ ロックリクエストは、WRITE スレッドがロックを得て、それをリリースするまで待つ、という意味になります。WRITE ロックを待っているスレッドの前に、別のスレッドが READ ロックを取得するのを許容する為に、LOW_PRIORITY WRITE を利用する事ができます。READ ロックを持つスレッドが無い時に、時間が残っている事が確実である時だけ、LOW_PRIORITY WRITE ロックを利用しなければいけません。(例外:トランザクション モード(自動コミット = 0)の InnoDB テーブルに対しては、LOW_PRIORITY WRITE ロックは通常の WRITE ロックのように機能し、待機中の READ ロックに先行します。)
LOCK TABLES は次のように機能します。
ロックされる全てのテーブルを内部定義順に並べ替えます。ユーザの立場からは、この順番は定義されていません。
もしそのテーブルが読み込みと書き込みの両方についてロックされるなら、読み込みロックの前に書き込みロックを行ってください。
スレッドが全てのロックを得るまで、テーブル1つずつにロックをして下さい。
.この方法は、テーブル ロックにデッドロックが起きない事を保証します。:しかし、この方法について知っておかなければいけない事があります。もしテーブルに対して
LOW_PRIORITY WRITE ロックを利用していたら、MySQL は READ ロックを必要とするスレッドがなくなるまで、この特定のロックを待つ、という意味になります。スレッドが WRITE ロックを得て、ロック テーブル リストの中の次のテーブルのロックを得るのを待っている時、他の全てのスレッドは WRITE ロックが解除されるのを待ちます。もしこれが、ご利用のアプリケーションにとって深刻な問題となってしまったら、いくつかのテーブルをトランザクション
セーフ テーブルに変換する事を考慮するべきです。
テーブル ロックを待っているスレッドを終了させる為に、KILL を安全に使用する事ができます。詳しくは 項3. 「KILL 構文」 を参照してください。
INSERT が別のスレッドによって実行されてしまう為、INSERT DELAYED と共に利用しているテーブルをロック してはいけない という事に注意してください。
通常、全ての単一 UPDATE ステートメントはアトミックなので、テーブルをロックする必要はありません。別のスレッドが現在実行中の SQL ステートメントの邪魔をする事はできません。しかし、テーブルをロックする事が利益をもたらす場合もいくつかあります。
MyISAM テーブル セット上でたくさんの操作を行おうとしているのであれば、利用する予定のテーブルをロックしたほうが操作が速くできます。UNLOCK TABLES がコールされるまで、MySQL はロックされたテーブルのキー キャッシュをフラッシュしないので、MyISAM テーブルをロックすると、挿入、更新、削除のスピードを速くします。通常、キー キャッシュは各 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_statementWHERE customer_id=some_id; UNLOCK TABLES;
LOCK TABLES 無しで、別のスレッドが SELECT と UPDATE ステートメントの実行の間に、trans テーブル内に新しい行を挿入する事が可能です。
多くの場合、相対更新(UPDATE customer SET value=value+new_value)または LAST_INSERT_ID() 関数を利用する事で、LOCK TABLES の利用を避ける事ができます。詳しくは 項3. 「トランザクションとアトミックオペレーション」 を参照してください。
ユーザ レベルの通知ロック機能 GET_LOCK() と RELEASE_LOCK() を利用する事で、テーブルのロックを避ける事ができる場合があります。これらのロックははサーバ内のハッシュ テーブルの中に保存され、スピードを速くする目的で
pthread_mutex_lock() と pthread_mutex_unlock() を利用して実施されます。詳しくは 項 「その他の関数」 を参照してください。
ロックの規定に関しての更なる情報は 項 「MySQL のテーブルロック方法」 を参照してください。
注意:もしロックされたテーブル上に ALTER TABLE を利用すると、ロックが解除される可能性があります。詳しくは 項B. 「Problems with ALTER TABLE」 を参照してください。