SELECT…FOR UPDATEを使用する場合


119

背後のユースケースを理解するのを手伝ってくださいSELECT ... FOR UPDATE

質問1:次のSELECT ... FOR UPDATE使用例は良い例ですか?

与えられた:

  • 部屋[id]
  • タグ[ID、名前]
  • room_tags [room_id、tag_id]
    • room_idとtag_idは外部キーです

アプリケーションは、すべての部屋とそのタグをリストする必要がありますが、タグのない部屋と削除された部屋を区別する必要があります。SELECT ... FOR UPDATEを使用しない場合、次のことが起こります。

  • 最初は:
    • 部屋には [id = 1]
    • タグに含まれる [id = 1, name = 'cats']
    • room_tagsには [room_id = 1, tag_id = 1]
  • スレッド1: SELECT id FROM rooms;
    • returns [id = 1]
  • スレッド2: DELETE FROM room_tags WHERE room_id = 1;
  • スレッド2: DELETE FROM rooms WHERE id = 1;
  • スレッド2:[トランザクションをコミットします]
  • スレッド1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • 空のリストを返します

スレッド1は、部屋1にはタグがないと考えますが、実際には部屋は削除されています。この問題を解決するには、スレッド1をSELECT id FROM rooms FOR UPDATEにする必要があります。これにより、roomsスレッド1が完了するまでスレッド2が削除されなくなります。あれは正しいですか?

質問2:場合は1が使用するSERIALIZABLEトランザクション分離が対READ_COMMITTEDSELECT ... FOR UPDATE

回答は移植可能であることが期待されます(データベース固有ではありません)。それが不可能な場合は、その理由を説明してください。


2
どのRDBMSを使用していますか?
Quassnoi 2013年

2
@Quassnoiは、質問の最後で述べたように、ポータブル(データベース固有ではない)ソリューションを探しています。
ギリ2013年

2
オプションREPEATABLE_READREAD_COMMITTEDさらにはポータブルオプションですか?私がこれらの結果を得る唯一の結果は、MSSQLサーバーに関するものです
Billy ONeal

3
@BillyONeal:分離モードでは、許可されていない癖が表示されないことが保証されますが、許可されている癖については何も言われません。つまり、たとえばREAD COMMITTEDモードを設定しても、実際に別のトランザクションによってコミットされたレコードが表示されるかどうかは定義されません。コミットされていないレコードが表示されないことが確認されるだけです。
Quassnoi 2013年

3
select ... for update上はroomsまだできるようになりますroom_tags彼らは別々のテーブルであるため、削除します。for update条項がからの削除を防止するかどうかを尋ねるかrooms
Chris Saxon

回答:


84

部屋とタグの間の一貫性を実現し、削除された部屋が戻されないことを保証する唯一のポータブルな方法は、でそれらをロックすることSELECT FOR UPDATEです。

ただし、一部のシステムでは、ロックは並行性制御の副作用であり、FOR UPDATE明示的に指定しなくても同じ結果が得られます。


この問題を解決するには、スレッド1をSELECT id FROM rooms FOR UPDATEにする必要があります。これにより、roomsスレッド1が完了するまでスレッド2が削除されなくなります。あれは正しいですか?

これは、データベースシステムが使用している同時実行制御によって異なります。

  • MyISAMin MySQL(および他のいくつかの古いシステム)は、クエリの実行中はテーブル全体をロックします。

  • ではSQL ServerSELECTクエリは調査したレコード/ページ/テーブルに共有ロックを設定し、DMLクエリは更新ロックを設定します(後で排他ロックに昇格または共有ロックに降格されます)。排他ロックは共有ロックと互換性がないため、SELECTまたはDELETEクエリは、別のセッションがコミットするまでロックされます。

  • 使用のデータベースではMVCC(のようなOraclePostgreSQLMySQLInnoDB)、DMLクエリは、(1または別の方法で)レコードのコピーを作成し、一般読者は作家やその逆をふさがないようにしてください。これらのデータベースの場合、SELECT FOR UPDATEは便利です。同じように、別のセッションがコミットするまでSELECT、いずれかまたはDELETEクエリをロックSQL Serverします。

とき1を使うべきREPEATABLE_READ対トランザクション分離をREAD_COMMITTED持ちますかSELECT ... FOR UPDATE

一般に、REPEATABLE READ幻の行(変更されるのではなく、別のトランザクションで表示または非表示になった行)を禁止しません。

  • ではOracle以前のPostgreSQLバージョンでは、REPEATABLE READ実際の同義語ですSERIALIZABLE。基本的に、これはトランザクションが開始した後に加えられた変更をトランザクションが認識しないことを意味します。したがって、この設定では、最後のThread 1クエリは部屋が削除されていないかのように部屋を返します(希望どおりである場合とそうでない場合があります)。削除された部屋を表示したくない場合は、行をロックする必要がありますSELECT FOR UPDATE

  • ではInnoDBREPEATABLE READSERIALIZABLEは異なりSERIALIZABLEます。モードのリーダーは、評価するレコードに次のキーのロックを設定しDML、それらの同時実行を効果的に防止します。したがってSELECT FOR UPDATE、シリアライズ可能モードでの必要はありませんが、REPEATABLE READまたはで必要READ COMMITEDです。

分離モードの標準では、クエリに特定の癖が見られないことが規定されていますが、その方法は定義されていないことに注意してください(ロックの有無、MVCCまたはその他の方法で)。

「必要ない」と言ったときは、「SELECT FOR UPDATE特定のデータベースエンジンの実装による副作用のために」本当に追加する必要がありました。


1
最後のポイントは問題の核心であると私は思います:「シリアライズ可能モードではSELECT FOR UPDATEは必要ありませんが、REPEATABLE READまたはREAD COMMITEDでは必要です」。
Colin 't Hart

あなたが正しい。2番目の質問では、SERIALIZABLEを使用する場合と使用する場合を比較READ_COMMITTEDする必要がありSELECT ... FOR UPDATEます。この更新された質問を反映するように回答を更新していただけますか?
ギリ2013年

1
@Gili:「SELECT FOR UPDATEシリアライズ可能モードでは必要ありません」、とInnoDB。他のMVCCシステムでは、この2つは同義語であり、が必要SELECT FOR UPDATEです。
Quassnoi 2013年

1
コリンの投稿はあなたの答えよりも私の特定の質問によく答えていると思いますが、あなたが提供したすべての参照に感謝します。2つを最もよく組み合わせた回答を受け入れます(特定の回答を上に、以下の参照をサポート)。
ギリ2013年

This depends on the concurrency control your database system is using:あなたは髪を分割していると思います。以下にリストするすべてのケースは、部屋がSELECTトランザクションの終わりまで削除されないことを示しています。それで、答えは単にYes以下のサポート参照であるはずではありませんか?
ギリ2013年

33

短い答え:

Q1:はい。

Q2:どちらを使用してもかまいません。

長い答え:

A select ... for updateは(それが意味するように)特定の行を選択しますが、現在のトランザクションによってすでに更新されているかのように(またはIDの更新が実行されたかのように)それらをロックします。これにより、別のトランザクションがこれらの行を変更することなく、現在のトランザクションで再度更新してコミットすることができます。

別の見方をすると、次の2つのステートメントがアトミックに実行されているかのようです。

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

の影響を受ける行my_conditionがロックされているため、他のトランザクションが行を変更することはできません。したがって、トランザクション分離レベルでは、ここでは違いはありません。

トランザクション分離レベルはロックとは独立していることにも注意してください。別の分離レベルを設定しても、トランザクションによってロックされている別のトランザクションの行をロックして更新することはできません。

トランザクション分離レベルが保証するのは(異なるレベルで)、トランザクションの進行中のデータの一貫性です。


1
What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.分離レベルはトランザクション中に発生すること影響を与えないことを誤って意味していると思います。このセクションを改訂し、トランザクション中に表示される(または表示されない)ものにどのように影響するかについて詳しく説明することをお勧めします。
ギリ2013年

1
あなたの投稿は、Quassnoiの質問よりも私の特定の質問によく答えていると思いますが、彼が提供したすべての参照に感謝します。私は2つを最もよく組み合わせた回答を受け入れます(特定の回答を上に、以下の参照をサポート)。
ギリ2013年

ロックと分離は、同じように複雑です。それについて知識を得るための本はありますか?
Chao
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.