1. ホーム
  2. mysql

[解決済み] SELECT ... FOR UPDATEはいつ使う?

2022-03-07 09:27:20

質問

このような場合、どのようにすればよいのでしょうか? SELECT ... FOR UPDATE .

質問1 : 以下のような場合は、良い例でしょうか? SELECT ... FOR UPDATE を使うべきですか?

与えられた。

  • rooms[id]です。
  • タグ[id, name]
  • room_tags[部屋番号, タグ番号]を指定します。
    • room_id と tag_id は外部キーです。

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

  • 最初は
    • 部屋には [id = 1]
    • タグを含む [id = 1, name = 'cats']
    • ルームタグを含む [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.room_id = 1 AND tags.id = room_tags.tag_id;
    • 空のリストを返す

今、スレッド 1 は部屋 1 にはタグがないと思っていますが、実際には部屋は削除されています。この問題を解決するために、スレッド 1 は SELECT id FROM rooms FOR UPDATE から削除されるのを防ぐことができます。 rooms Thread 1が終了するまで。これでよいのでしょうか?

質問2 : どのような場合に SERIALIZABLE トランザクションの分離と READ_COMMITTEDSELECT ... FOR UPDATE ?

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

解決方法は?

ルームとタグの間に一貫性を持たせ、削除されたルームが二度と戻ってこないようにする唯一のポータブルな方法は、ルームをロックすることです。 SELECT FOR UPDATE .

しかし、システムによってはロックは同時実行制御の副次的なものであり FOR UPDATE を明示的に指定します。


この問題を解決するために、スレッド 1 は SELECT id FROM rooms FOR UPDATE から削除されるのを防ぐことができます。 rooms Thread 1が終了するまで。これでよいのでしょうか?

これは、データベースシステムが使用している並行処理制御に依存します。

  • MyISAMMySQL (そして他のいくつかの古いシステムも)クエリの間、テーブル全体をロックします。

  • SQL Server , SELECT クエリは検査したレコード/ページ/テーブルを共有ロックするのに対し DML クエリは更新ロック (これは後に排他ロックに昇格するか、共有ロックに降格します) を配置します。排他ロックは共有ロックと互換性がないため、以下のどちらかを行います。 SELECT または DELETE クエリは、他のセッションがコミットするまでロックされます。

  • を使用するデータベースでは MVCC (例えば Oracle , PostgreSQL , MySQLInnoDB ), a DML クエリはレコードのコピーを作成し、一般に読者は書込み者をブロックせず、その逆もまた然りです。これらのデータベースでは SELECT FOR UPDATE をロックすることができます。 SELECT または DELETE と同じように、他のセッションがコミットするまで、クエリを実行することができます。 SQL Server がそうである。

どのような場合に使用するのでしょうか? REPEATABLE_READ トランザクションの分離と READ_COMMITTEDSELECT ... FOR UPDATE ?

一般的に REPEATABLE READ は、ファントムロー(変更されるのではなく、別のトランザクションで現れたり消えたりした行)を禁じない。

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

  • InnoDB , REPEATABLE READSERIALIZABLE の読者は別のものです。 SERIALIZABLE モードでは、評価するレコードにネクストキー・ロックが設定されるため、効果的に DML を搭載しています。だから SELECT FOR UPDATE では必要ですが、シリアライズモードでは REPEATABLE READ または READ COMMITED .

分離モードに関する標準では、クエリに特定の癖を見せないようにすることを規定していますが、どのように(ロックや MVCC など)。

というのは、このような場合です。 SELECT FOR UPDATE というのも、あるデータベースエンジンの実装には副作用があるためです。