MySQL
MySQLのロックテーブル
サーチ…
構文
LOCK TABLESテーブル名[READ |書きます]; //テーブルをロックする
UNLOCK TABLES; //テーブルをロック解除する
備考
ロッキングは並行性の問題を解決するために使用されます。ロッキングはトランザクションを実行する場合にのみ必要です。トランザクションは、まずデータベースから値を読み取り、後でその値をデータベースに書き込みます。自己完結型の挿入、更新、または削除操作には、ロックは必要ありません。
利用可能なロックには2種類あります
READ LOCK - ユーザーがテーブルからのみ読み取り中の場合。
WRITE LOCK - ユーザーが表の読み取りと書き込みの両方を行っているとき。
ユーザーが表にWRITE LOCK
を保持している場合、その表を他のユーザーが読み書きすることはできません。ユーザーがテーブルにREAD LOCK
を保持すると、他のユーザーもREAD LOCK
読み取ったり保持したりすることができますが、そのテーブルにWRITE LOCK
を書き込みまたは保持することはできません。
デフォルトのストレージエンジンがInnoDBの場合、MySQLは自動的に行レベルのロックを使用するため、複数のトランザクションが同じテーブルを読み書きのために同時に使用することができます。
InnoDB以外のすべてのストレージエンジンでは、MySQLはテーブルロックを使用します。
テーブルロックの詳細はこちら
MySQLのロック
テーブルロックはENGINE=MyISAM
重要なツールですが、 ENGINE=InnoDB
ほとんど役に立ちません。 InnoDBでテーブルロックを使用するように誘惑されている場合は、トランザクションの操作方法を再考する必要があります。
MySQLは、他のセッションと協調してテーブルにアクセスする目的で、またはセッションが排他的アクセスを必要とする期間中に他のセッションがテーブルを変更することを防ぐ目的で、クライアントセッションでテーブルロックを明示的に取得できます。セッションは、自分自身のロックのみを取得または解放できます。 1つのセッションは別のセッションのロックを取得したり、別のセッションが保持するロックを解除することはできません。
ロックを使用すると、トランザクションをエミュレートしたり、テーブルを更新するときに速度を向上させることができます。詳細については、このセクションの後半で説明します。
コマンド: LOCK TABLES table_name READ|WRITE;
1つの表にロック・タイプのみを割り当てることができます。
例(READ LOCK):
LOCK TABLES table_name READ;
例(WRITE LOCK):
LOCK TABLES table_name WRITE;
ロックが適用されているかどうかを確認するには、次のコマンドを使用します。
SHOW OPEN TABLES;
すべてのロックをフラッシュ/削除するには、次のコマンドを使用します。
UNLOCK TABLES;
例:
LOCK TABLES products WRITE:
INSERT INTO products(id,product_name) SELECT id,old_product_name FROM old_products;
UNLOCK TABLES;
上記の例では、外部接続はテーブルのロックを解除するまで製品テーブルにデータを書き込むことができません
例:
LOCK TABLES products READ:
INSERT INTO products(id,product_name) SELECT id,old_product_name FROM old_products;
UNLOCK TABLES;
上記の例では、外部接続はテーブルのロックを解除するまで商品テーブルからデータを読み取ることができません
行レベルのロック
テーブルがInnoDBを使用している場合、MySQLは自動的に行レベルのロックを使用し、複数のトランザクションが同じテーブルを読み書きのために同時に使用できるようにします。
2つのトランザクションが同じ行を変更しようとしていて、両方とも行レベルのロックを使用している場合、トランザクションの1つはもう一方のトランザクションが完了するまで待機します。
行レベルのロックは、変更が必要な行ごとにSELECT ... FOR UPDATE
文を使用しても取得できます。
行レベルのロックを詳細に説明する2つの接続を検討してください
接続1
START TRANSACTION;
SELECT ledgerAmount FROM accDetails WHERE id = 1 FOR UPDATE;
接続1では、 SELECT ... FOR UPDATE
文によって取得された行レベルのロックです。
接続2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1;
接続2で同じ行を更新しようとすると、接続1でトランザクションが終了するのを待つか、 innodb_lock_wait_timeout
設定に従ってエラーメッセージが表示されます。デフォルトは50秒です。
Error Code: 1205. Lock wait timeout exceeded; try restarting transaction
このロックの詳細を表示するには、 SHOW ENGINE INNODB STATUS
実行します
---TRANSACTION 1973004, ACTIVE 7 sec updating
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 4, OS thread handle 0x7f996beac700, query id 30 localhost root update
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1
------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED:
接続2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 250 WHERE id=2;
1 row(s) affected
しかし、接続2の他の行を更新する際にエラーが発生することはありません。
接続1
UPDATE accDetails SET ledgerAmount = ledgerAmount + 750 WHERE id=1;
COMMIT;
1 row(s) affected
トランザクションは接続1でコミットされるため、行ロックは解放されます。
接続2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1;
1 row(s) affected
Connection 1がトランザクションを終了して行ロックを解放した後、Connection 2でエラーなしで更新が実行されます。