0

次の 2 つの MySQL ステートメントを実行します。

1) SELECT * FROM table1 WHERE field1=val1 FOR UPDATE;
2) UPDATE table1 SET field2=val2 WHERE field1=val1;

2 番目のステートメントが、最初のステートメントによって返された行を正確に変更することが重要です (追加の行や 1 つ少ない行はありません)。したがって、トランザクションを auto_commit=false で実行し、select ステートメントの「for update」バージョンを使用します。

「For Update」は、返されるすべての行をロックするため、2 番目のステートメントが実行されたときに元の状態になります。しかし、挿入はどうですか?別のスレッドが field1=val1 の間に新しい行を挿入し、2 番目のステートメントによって変更される可能性はありますか?

もう 1 つの質問: 2 番目のステートメントが行自体を変更するのではなく、次のようなことを行う場合、違いはありますか?

3) INSERT INTO table2 (SELECT * FROM table1 WHERE field1=val1)

(3) が (1) と同じトランザクションにある場合、両方の選択がまったく同じ要素を返すことが保証されますか?

編集:

私は InnoDB を使用しており、次のキーのロックとギャップのロックに関するいくつかの記事を読みました。私の知る限り、(1) の実行中、InnoDB は選択された行だけでなく、アクセスされたインデックスもロックしていました。

では、列「field1」にインデックスがある場合、この問題は発生しないと言うのは正しいでしょうか? インデックスがない場合はどうなりますか?じゃあ違うの?

4

1 に答える 1

1

mySQL のケースを完全に説明することはできませんが、むしろ疑っています。SELECT FROM ... FOR UPDATE実行する (ように見える)唯一のことは、他のトランザクションが指定された行のセットを変更できないようにすることです。将来のステートメントを制限しません。そのステートメントが実行される前に別の行が (別のトランザクションによって) 挿入された場合UPDATE、それも更新されます。
言うまでもなく、あなたが与えた 3 番目の声明も同じ問題の餌食になります。

ここで実際に何をしようとしていますか?これを達成する別の方法がある可能性があります。たとえば、ある種の「insertedAt」タイムスタンプがある場合、おそらくそれを追加条件として追加できます。

于 2012-11-16T16:56:23.727 に答える