3

分離レベルはSELECTSにのみ適用され、UPDATESには適用されませんか?

SELECTSのさまざまな分離動作を示したシナリオ

1) 0:00 Thread A runs a query that returns 1000 rows that takes 5 minutes to complete
2) 0:02 Thread B runs a query that returns the same 1000 rows
3) 0:05 Thread A updates the last 1 rows in this result set and commits them
4) 0:07 Thread B's query returns* 

分離レベルに応じて、#4の結果セットにはスレッドAの変更が含まれるか含まれません。UPDATESについても同じですか?

シナリオの例を次に示します。

Thread A: UPDATE ... WHERE primary_key = 1234 AND version = 5
Thread B: UPDATE ... WHERE primary_key = 1234 AND version = 5

スレッドAとスレッドBの両方が同時にトランザクションを入力し、スレッドBがスレッドAの後に更新を実行する場合、スレッドBの更新は失敗しますか、それともバージョン5のレコードを「認識」して成功しますか?

データベースに依存しますか?例:Oracle vs MySql vs PostgreSQL?

4

2 に答える 2

7

次のような多くの ORM で使用される「楽観的ロック」パターンを表示するつもりであると仮定します。

Thread A: UPDATE ... SET ..., version = 6 WHERE primary_key = 1234 AND version = 5
Thread B: UPDATE ... SET ..., version = 6 primary_key = 1234 AND version = 5

次に、すべての賢明な分離レベルで(READ UNCOMMITTEDについて100%確信が持てません-ほとんどのDBはそれをサポートしていません)、スレッドBは行に一致せず、効果がありません。

たとえば PostgreSQL では、スレッド B は最初は A と同じ行に一致しますが、スレッド A がコミットまたはロールバックするまで、行更新ロックでブロックされます。この時点で、条件を再チェックし、スレッド A がコミットされた場合に一致しなくなったことを検出するため、何もしません。行のロックは、この特定のケースではシリアライゼーションの競合が発生しないことを意味します。

健全なデータベースでは、2 つの更新のうち 1 つだけが成功します。2 つ目は、分離レベルと DB の実装に応じて、ゼロ行に一致するか、シリアライゼーションの失敗で中止されます。これは、少なくとも 5.5 のInnoDB を使用する MySQL でも当てはまります (この回答の詳細な説明とデモを参照してください)。MyISAM を使用している場合、正確性と信頼性は明らかに大きな問題ではありません ;-)

UPDATEs とsに異なる分離ルールを適用するデータベースは知りませんSELECT。結局のところ、 はその句、サブクエリなどUPDATEに対して と同じ分離保証を必要とします。s できないものをデッドロックすることができます (PostgreSQL では、どうやら MySQL+InnoDB ではできるようです)。とは異なり、は分離モードでシリアル化に失敗する可能性がありますが、可視性ルールは同じです。WHERESELECTUPDATESELECTSELECTUPDATESERIALIZABLE

同時実行制御に関する PostgreSQL のドキュメントでは、これについて非常によく説明されています。

于 2012-11-18T02:28:43.687 に答える
1

Oracle では、ステートメント レベルの一貫性があります。

Oracle Databaseは常に文レベルの読取り一貫性を適用します。これにより、単一の問合せによって返されるデータがコミットされ、単一の時点に関して一貫性が保たれます。

これは、SELECT の例が Oracle ではこのように機能しないことを意味します。スレッド B は、ステートメントの開始時と同様に、select からの結果を返します。これは、Oracle が、クエリが開始されたときと同じように、元に戻すデータから過去のブロックを再作成する可能性があることを意味します。ポイント (3) の更新によって行われた変更は、結果には存在しません。

選択クエリは、コミットされたとしても、開始後に行われたトランザクションの変更を認識しません。

更新も同様に機能しますが、追加の作業が必要です。すべての更新/削除は、標準のポイント イン タイムの一貫性を備えた標準の SELECT で始まりますが、ブロックは で要求されCURRENT MODEます。これは、変更する必要があるブロック バージョンが最後のバージョンであるためです。さらに、最後のものは、ブロックの現在のロックに関する情報を含むも​​のでもあります。Tom Kyte は、 DML の優れたアナロジーを持っています (削除と更新で同じように機能します)。

削除が次のように処理されていると考えてください。

for x in ( select rowid from emp )   --- CONSISTENT GETS
loop
   delete from emp where rowid = x.rowid;  --- CURRENT MODE GETS
end loop;

SELECT を更新で置き換えると、シナリオで何が起こるでしょうか?

まず、行がまだロックされている (ポイント (3) のトランザクションがコミットされていない) 場合、ポイント (4) の更新は (3) コミットまたはロールバックするまで待機します。

トランザクションがコミットされていて、SERIALIZABLEトランザクションが分離されている場合は、もちろんエラーが発生します。トランザクションの開始以降に変更されたデータを変更したくありません (これらの変更は目に見えないため)。

READ COMMITEDは、興味深い、直感的ではない展開があります。Oracle は、変更された行を取得すると、クエリの開始後にデータが変更されたことを認識します。これは一貫性がないため、オラクルは現在更新を処理できません (さらに、更新が失われる可能性があります)。そのため、この別の askTom スレッドで説明されているように、 Oracleはクエリを再開します。

結果セットは一貫していますが、再起動時には一貫している可能性があります。

今回は(うまくいけば)一貫した方法ですべての行を取得する2番目の「選択」を取得します。ロックは行ごとに配置されるため、最初のパスで見つかったすべての行は引き続き使用できるはずです (最初のパスと 2 番目のパスの間で別のトランザクションによって変更されることはありません)。

于 2012-11-19T12:05:29.213 に答える