LOCK TABLES tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE} ...]
...
UNLOCK TABLES
LOCK TABLES では、現在のスレッドのテーブルがロックされます。UNLOCK TABLES では、現在のスレッドが保有しているロックが解除されます。現在のスレッドが別の LOCK TABLES を発行したり、サーバへの接続が閉じられると、現在のスレッドがロックしているテーブルのロックはすべて暗黙的に解除されます。
MySQL 4.0.2 で LOCK TABLES を使用するには、関連するテーブルに対するグローバルな LOCK TABLES 権限と SELECT 権限が必要です。MySQL 3.23 では、関連するテーブルに対する SELECT、insert、DELETE、UPDATE の各権限が必要です。
LOCK TABLES を使用する主な理由は、トランザクションをエミュレートしたり、テーブルの更新時に処理を迅速化したりするためです。これについては、詳しく後述します。
あるスレッドがテーブルに対する READ ロックを取得すると、そのスレッド(および他のすべてのスレッド)はそのテーブルからの読み取りのみ実行できるようになります。あるスレッドがテーブルに対する WRITE ロックを取得すると、ロックを保有しているスレッドだけがそのテーブルからの読み取りやテーブルへの書き込みを実行できるようになります。他のスレッドはブロックされます。
READ LOCAL と READ の違いは、READ LOCAL の場合、ロックの保有中に、コンフクリトを発生させない INSERT ステートメントを実行できることです。しかし、ロックの保有中にデータベースを MySQL の外部で操作しようとする場合は、この機能を使用できません。
LOCK TABLES の使用時には、使用するテーブルをすべてロックし、またクエリで使用するエイリアスと同じ名前を使用する必要があります。1 つのクエリで同じテーブルを何度も指定する(エイリアスを使用して)場合は、各エイリアスに対してロックを取得しなければなりません。
更新をできるだけ早く処理するために、WRITE ロックは、通常、READ ロックより優先されます。そのため、あるスレッドが READ ロックを取得し、別のスレッドが WRITE ロックを要求している場合、後続の READ ロック要求は、WRITE スレッドがロックを取得し、その後そのロックを解除するまで待機します。LOW_PRIORITY WRITE ロックでは、そのスレッドが WRITE ロックを取得する前に、他のスレッドが READ ロックを取得することができます。その間、LOW_PRIORITY WRITE ロックを発行したスレッドは待機します。LOW_PRIORITY WRITE は、READ ロックを取得しようとするスレッドが皆無になるときがあるとわかっている場合にのみ使用してください。
LOCK TABLES は次のように機能します。
ロックするテーブルを内部定義された順序(ユーザから見た場合は未定義)でソートする。
読み取りロックと書き込みロックでテーブルがロックされる場合は、読み取りロックより書き込みロックを優先する。
一度に 1 つのテーブルをロックし、スレッドがすべてのロックを取得するまでロック処理を繰り返す。
このポリシーによって、テーブルのロックでデッドロックの発生が回避されます。ただし、このスキーマに関しては注意すべき点が他にもあります。
MySQL でテーブルに対して LOW_PRIORITY WRITE ロックを使用した場合、そのスレッドは READ ロックを要求する他のスレッドがなくなるまでの間のみ待機し、READ を要求する他のスレッドがなくなった時点でロックを取得します。そのスレッドで WRITE ロックを取得した後に、ロックテーブルリストの次のテーブルのロックを取得しようと待機しているときには、他のスレッドの方がいずれも WRITE ロックが解除されるまで待つことになります。これによってアプリケーションで深刻な問題が発生する場合は、一部のテーブルをトランザクションセーフテーブルに変換することを検討してください。
テーブルのロックを取得しようと待機しているスレッドを、安全な方法で強制終了するには、KILL を使用します。 See 項4.6.7. 「KILL 構文」。
注意: INSERT DELAYED で使用しているテーブルは、ロックすべきではありません。なぜなら、この場合、INSERT が独立したスレッドで実行されるためです。
個々の UPDATE ステートメントでは、いずれも処理が原子的に行われるため、通常、テーブルをロックする必要はありません。現在実行中の SQL ステートメントが、他のスレッドによって妨害されることはまったくありません。しかし、次に示すように、テーブルをロックする必要が生じる場合もいくつかあります。
一連のテーブルに対して多くの操作を実行する場合、使用するテーブルをロックした方が処理がはるかに迅速になる。当然ながら、この場合の短所は、READ ロックされているテーブルについては、どのスレッドも(ロックを保有しているスレッドも含む)このテーブルを更新できなくなり、WRITE ロックされているテーブルについては、ロックを保有しているスレッド以外、どのスレッドもこのテーブルを読み取れなくなることである。
LOCK TABLES の使用時にいくつかの面で処理が迅速になる理由は、MySQL でキーのキャッシュが UNLOCK TABLES が呼び出されるまでフラッシュされないためである(通常、キーのキャッシュは各 SQL ステートメントの後にフラッシュされる)。それによって、MyISAM テーブルに対する挿入、更新、削除処理が迅速化される。
トランザクションをサポートしていないストレージエンジンを MySQL で使用している場合、SELECT と UPDATE の間に他のスレッドに割り込まれないようにするには、LOCK TABLES を使用する必要がある。次の例では、安全に処理を実行するために LOCK TABLES を発行する必要がある。
mysql> LOCK TABLES trans READ, customer WRITE;
mysql> SELECT SUM(value) FROM trans WHERE customer_id=some_id;
mysql> UPDATE customer SET total_value=sum_from_previous_statement
-> WHERE customer_id=some_id;
mysql> 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() で実装されます。
See 項6.3.6.2. 「その他の各種関数」。
ロックポリシーの詳細については、項5.3.1. 「MySQL のテーブルロック方法」 を参照してください。
全データベースの全テーブルを読み取りロックでロックするには、FLUSH TABLES WITH READ LOCK コマンドを使用します。See 項4.6.4. 「FLUSH 構文」。特定の時点のスナップショットを取ることができる、Veritas などのファイルシステムを使用している場合には、このコマンドがバックアップを作成するときに非常に役立ちます。
注意: LOCK TABLES はトランザクションセーフではありません。アクティブなトランザクションは、テーブルロックの試行前に暗黙的にコミットされます。