InnoDB ストレージ エンジンを使用する Web アプリケーションでは、次のシナリオでデータベース ロックを適切に利用できませんでした。
3 つのテーブルがあり、それらを と とaa
呼びar
ますai
。
aa
基本レコード、たとえば記事を保持します。ar
は、各aa
レコードに関連する情報と、 と の関係aa
を保持しar
ます1:m
。
のレコードは、 からのレコードが最初に読み込まar
れたときに保存されます。問題は、2 つの要求が (ほぼ) 同じ時刻に開始されて (関連するレコードが に保存されていない) レコードを読み取ると、レコードが重複することです。aa
aa
ar
ar
状況を理解するのに役立つ疑似コードを次に示します。
aa
要求されたレコードを読み取ります。ar
テーブルをスキャンして、指定されたaa
レコードに既に何かが格納されているかどうかを調べます。(そうではないと仮定します。)特定のレコード
ai
を何に保存するかを調べるには、相談してください。(やや無関係に思えますが、ロックにも関与する必要があることがわかりました...間違っている可能性があります。)ar
aa
ai
数行挿入して
ar
これが私が達成したいことです:
aa
要求されたレコードを読み取ります。
TRANSACTIONS LOCKar
を使用するかどうかに関係なく、読み取りを試行する後続のリクエストは、この時点でar
1 つが終了するまで待機します。
ar
テーブルをスキャンして、指定されたaa
レコードに既に何かが格納されているかどうかを調べます。(そうではないと仮定します。)問題は、2 つの同時要求の場合、指定されたレコードにレコードが存在しないことを検出し、両方とも同じ行を 2 回挿入することです。ar
aa
それ以外の場合、このシーケンスは中断され、INSERT は発生しません。特定のレコード
ai
を何に保存するかを調べるには、相談してください。(やや無関係に思えますが、ロックにも関与する必要があることがわかりました...間違っている可能性があります。)ar
aa
ai
数行挿入して
ar
ロックオンを解除するar
単純に思えますが、重複を回避できませんでした。Bash シェル (wget を使用) で単純なコマンドからの同時要求をテストしています。
私は、 http: //dev.mysql.com/doc/refman/5.5/en/innodb-lock-modes.htmlとhttp://dev.mysql. com/doc/refman/5.5/en/innodb-locking-reads.htmlロックを利用するいくつかの方法を試しましたが、まだ運がありません。
テーブル全体をar
ロックしたい (INSERT が複数のリクエストから発生するのを防ぎたいため) このテーブルとのやり取りをさらに試行して、最初のロックが解除されるまで待機させます。しかし、ドキュメントには「テーブル全体」がロックされているという言及が 1 つだけありますが (最初のリンク先ページのインテンション ロックのセクション)、それについては詳しく説明されていないか、それを達成する方法を理解できませんでした。
誰かが正しい方向を指すことができますか?