2017-06-19 5 views
2

以下は、MySQL 5.6のトランザクションでInnoDBテーブルを使用してすべてのステートメントが実行されることを前提としています。また、col1とcol2が一緒になって一意のキーを形成します。FOR UPDATEを伴うMySQL読み取りロック

セッション1でSELECT * FROM table WHERE col1 = 1 AND col2 = 1 FOR UPDATEを実行すると、その行のデータとその行の排他ロックが取得されます。したがって、セッション2で同じステートメントを実行すると、何かをする前にロックが解除されるか、タイムアウトになるまで待機します。ここまでは順調ですね。

-- Session 1 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.01 sec) 
-- Do whatever else takes some time 

-- Session 2 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1; 
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 

私はそれを期待するとして、この動作は次のとおりです。

SELECT ... FOR UPDATEが空のセットを返し、私たちは別のセッションで何かを挿入しようと想定しています。我々はこれを行う場合は、:

-- Session 1 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.00 sec) 

-- Session 2 
mysql> START TRANSACTION; 
Query OK, 0 rows affected (0.00 sec) 

mysql> SELECT * FROM `table` WHERE `col1` = 1 AND `col2` = 1 FOR UPDATE; 
Empty set (0.00 sec) 

mysql> INSERT INTO `table` SET `col1` = 1, `col2` = 1; 
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 

さて、私が実際にしたいことは同じで、第2 SELECT ... FOR UPDATEですが(まだ存在しない)行は同じようにINSERT INTO意志をお待ちしております。テーブル全体をロックせずにこれを達成するにはどうすればよいですか。これは、他の行がアクセス可能である必要があるため許容できません。

+0

SQLステートメントによってロックが設定されているものに大きな影響を与えないので、私はあなたが望むことはできないと思います。 innodbを使用すると、insert文は常に排他的な意図のロックを生成し、updateのための選択も排他的なロックを要求します。 – Shadow

+0

問題は、特定の列がすでに存在する場合はセッション1が 'select for update'でチェックし、そうでない場合は作成して保存する競合状態です。 セッション2では、処理がPHPスクリプトでMySQLの外部で行われるため、すぐにその行があることはわかりません。行が実際に存在するには数ミリ秒かかることがあります。私はもちろんこれをハックすることができますが、私が上記のようにそれを行うことは、はるかにクリーンなソリューションIMHOになります。 – Anpan

+0

私はあなたの質問を理解していますが、MySQLの解決策は、影響を受けるステートメントに対して異なる種類のロックを使用することです。 select文でupdate節を使用している限り、あなたは立ち往生しています。明らかに、共有モードでロックを使用した場合、その動作は異なります。しかし、私はあなたのビジネス要件については何も知らないので、このアプローチがあなたに役立つかどうかはわかりません。 – Shadow

答えて

0

トランザクションを数秒以上実行させると、問題が発生することがあります。速く完了するようにコードを書いてください。あなたの最初のコードサンプルはinnodb_lock_wait_timeoutではありません。デフォルトは50秒です。

あなたが持っている2つの異なるものなど、エラーを捕らえて回避行動を取るコードを書く必要があります。アクションは、通常、トランザクション全体を開始することです。それまでに、他のセッションは、何をしていたとしても終了している可能性が高いので、「即座に」進むべきです。

ユーザーがWebページ間で数分間バウンスするショッピングカートなど、本当に長いロックが必要な場合は、InnoDBのトランザクションロックではなく、「ロック」に他のメカニズムを使用する必要があります。

あなたのアプリケーションについてさらに詳しく説明したい場合は、これ以上話し合うことができます。

+0

これは実際に私が説明の目的で手作業で手作業で行っていたものです。実際のユースケースでは、おそらくミリ秒未満で表示された方法でデータにアクセスする2つの並列プロセスについて話しています。 – Anpan

+0

ミリ秒で 'COMMIT'ですか? 「時間がかかります」と私を捨てました。 –

+0

実際、コミットまでの全プロセスはミリ秒単位で完了します。問題は、2つのプロセスがほぼ同時に開始されることですが、もう1つのプロセスが終了するまで、この1つのプロセスを待機する必要があります。 – Anpan

関連する問題