非繰り返し読み取りとファントム読み取りの違いは何ですか?


154

反復不能読み取りとファントム読み取りの違いは何ですか?

ウィキペディアから分離(データベースシステム)の記事を読みましたが、いくつか疑問があります。以下の例では、何が起こりますか?反復不能読み取りファントム読み取り

トランザクションA
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
出力:
1----MIKE------29019892---------5000
トランザクションB
UPDATE USERS SET amount=amount+5000 where ID=1 AND accountno=29019892;
COMMIT;
トランザクションA
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1

別の疑問は、上記の例でどの分離レベルを使用する必要があるかです。なぜ?


回答:


165

ウィキペディアから(これには素晴らしい詳細な例があります):

トランザクションの途中で行が2回取得され、行の値が読み取り間で異なる場合、繰り返し不可の読み取りが発生します。

そして

幻像読み取りは、トランザクションの過程で2つの同一のクエリが実行され、2番目のクエリによって返される行のコレクションが最初のクエリと異なる場合に発生します。

簡単な例:

  • ユーザーAが同じクエリを2回実行します。
  • その間に、ユーザーBがトランザクションを実行してコミットします。
  • 繰り返し不可の読み取り:ユーザーAが照会したA行は、2回目の値が異なります。
  • 幻像読み取り:クエリのすべての行の前後で値は同じですが、異なる行が選択されています(Bが一部を削除または挿入したため)。例:select sum(x) from table;行が追加または削除された場合、影響を受ける行自体が更新されていなくても、異なる結果を返します。

上記の例では、どの分離レベルを使用しますか?

必要な分離レベルは、アプリケーションによって異なります。「並行性の低下」など、「より良い」分離レベルには高いコストがかかります。

あなたの例では、(主キーで識別される)単一の行からのみ選択するので、ファントムリードはありません。反復不可能な読み取りを行うことができるため、それが問題である場合は、それを防止する分離レベルが必要になる場合があります。Oracleでは、トランザクションAもSELECT FOR UPDATEを発行でき、トランザクションBはAが完了するまで行を変更できません。


6
私は本当に、このような構文のロジックを理解していない... A NON-反復可能読み取りがされたときに、読み取りが発生繰り返さ!(と異なる値が得られ)?? ...
serhio

14
@serhio "非反復可能"とは、値を一度読み取って結果としてxを取得し、次に再度読み取って結果としてyを取得できるため、2つの同じ結果を繰り返すことはできません(反復不能)その行の値は読み取りと読み込みの間に更新されたため、同じ行の個別のクエリ。
BateTech 2015

@Thilo反復可能読み取りが問題を引き起こす可能性があり、必要な場合の実際の使用例はありますか?
user104309 2017

PKが別のトランザクションで変更された場合はどうなりますか?その結果、ファントムリードが発生する可能性がありますか?(ほとんどの場合、奇妙なことですが、不可能ではありません。)
jpmc26

1
どちらも同じように聞こえる
sn.anurag

125

私がそれについて考えたい単純な方法は、次のとおりです。

繰り返し不可の読み取りとファントム読み取りは、トランザクションの開始後にコミットされ、トランザクションによって読み取られた、別のトランザクションからのデータ変更操作に関係しています。

繰り返し不可の読み取りは、トランザクションが別のトランザクションからコミットされたUPDATESを読み取る場合です。同じ行の値が、トランザクションの開始時とは異なるようになりました。

ファントムがコミットからの読み取り時に似ていますが、読み込みINSERTSおよび/またはDELETES別のトランザクションから。新しい行またはトランザクションを開始してから消えた行があります。

ダーティリードは、繰り返し不可のファントムリードにていますが、UNCOMMITTEDデータの読み取りに関連し、別のトランザクションからのUPDATE、INSERT、またはDELETEが読み取られ、他のトランザクションがまだデータをコミットしていない場合に発生します。「進行中」のデータを読み込んでいるため、完全ではなく、実際にコミットされることはありません。


4
トランザクションの分離レベルと同時実行性に関係しています。デフォルトの分離レベルを使用すると、ダーティリードを取得せず、ほとんどの場合、ダーティリードを回避します。ダーティ・リードを可能にする分離レベルまたはクエリヒント、あるいくつかのそのような別の接続から進行トランザクションのトラブルシューティングなどの場合より高い並行性を達成するために許容されるトレードオフであるか、またはエッジのケースに起因必要であるが、。ダーティリードのアイデアが「臭いテスト」に通らないことは良いことです。原則として、bcは回避する必要がありますが、目的はあります。
BateTech、2015

1
ここでの@PHPAvengerは、READ UNCOMMITTED分離レベルの使用例です。選択クエリと更新クエリの間にデッドロックが発生する可能性が常にあります(ここで説明)。選択クエリが複雑すぎてカバリングインデックスを作成できない場合は、デッドロックを回避するために、ダーティリードが発生するリスクのあるREAD UNCOMMITED分離レベルを使用する必要がありますが、トランザクションをロールバックしてダーティリードを気にする頻度はありません永続的ですか?
petrica.martinescu 2017年

1
@ petrica.martinescuダーティーリードによって引き起こされる問題は、トランザクションがロールバックされるかどうかだけではありません。ダーティリードは、保留中のトランザクションのデータがどのように変更されたかによって、非常に不正確な結果を返す可能性があります。一連の削除、更新、挿入を実行するトランザクションを想像してください。「コミットされていない読み取り」を使用してトランザクションの途中でデータを読み取ると、不完全になります。(SQL Serverの)スナップショット分離レベルは、コミットされていない読み取りよりもはるかに優れています。本番システムでコミットされていない読み取り分離レベルの有効なユースケースは、まれなIMOです。
BateTech 2018

2
@DiponRoy素晴らしい質問です。反復可能読み取り(RR)分離を使用する場合に実装されるロックは、選択された行で削除が発生しないようにする必要があります。私は長年にわたって2つのisoレベルのさまざまな定義を見てきましたが、主にファントムは返されるコレクション/#行の変更であり、RRは変更されている同じ行です。更新されたMS SQLドキュメントを確認したところ、削除するとRR以外の原因になる可能性がある(docs.microsoft.com/en-us/sql/odbc/reference/develop-app/…)ので、削除をグループ化しても安全だと思いますRRカテゴリも
BateTech 2018

2
@anir yes挿入と削除はダーティリードに含まれます。例:トランザクションを開始し、接続aに100行のうち2行を挿入すると、接続bはtrxがコミットされる前、および他の98行が追加される前にこれらの2行を読み取るため、請求書のすべての情報は含まれません。これは、挿入を含むダーティリードになります。
BateTech

28

この記事で説明したように、Non-Repeatable Read異常は次のようになります。

ここに画像の説明を入力してください

  1. アリスとボブは2つのデータベーストランザクションを開始します。
  2. Bob'sは投稿レコードを読み取り、title列の値はTransactionsです。
  3. アリスは、特定の投稿レコードのタイトルをACIDの値に変更します。
  4. アリスはデータベーストランザクションをコミットします。
  5. ボブが投稿レコードを再度読み取ると、このテーブル行の別のバージョンが表示されます。

、この記事についてのファントム読み取り、次のようにこの異常が起こることができることがわかります。

ここに画像の説明を入力してください

  1. アリスとボブは2つのデータベーストランザクションを開始します。
  2. Bob'sは、ID値が1の投稿行に関連付けられたすべてのpost_commentレコードを読み取ります。
  3. アリスは、ID値が1の投稿行に関連付けられた新しいpost_commentレコードを追加します。
  4. アリスはデータベーストランザクションをコミットします。
  5. ボブがpost_id列の値が1に等しいpost_commentレコードを再度読み取ると、この結果セットの別のバージョンが観察されます。

したがって、繰り返し不可の読み取りは単一の行に適用されますが、ファントム読み取りは、特定のクエリフィルタリング基準を満たすレコードの範囲に関するものです。


3
優れた視覚化@Vlad
dextermini 2018

23

現象を読む

  • ダーティリード:別のトランザクションからコミットされていないデータを読み取る
  • 繰り返し不可の読み取りUPDATE別のトランザクションのクエリからCOMMITTEDデータを読み取ります
  • ファントム読み取り:別のトランザクションから、INSERTまたはDELETEクエリからCOMMITTEDデータを読み取ります

:別のトランザクションからのDELETEステートメントも、特定の場合に繰り返し不可の読み取りを引き起こす可能性が非常に低くなります。これは、残念ながらDELETEステートメントが、現在のトランザクションが照会していたのとまったく同じ行を削除したときに発生します。しかし、これはまれなケースであり、各テーブルに数百万の行があるデータベースでは発生する可能性ははるかに低くなります。トランザクションデータを含むテーブルは、通常、実稼働環境ではデータ量が多くなります。

また、ほとんどのユースケースでは、実際のINSERTまたはDELETESではなく、UPDATESの方が頻繁なジョブである可能性があります(このような場合、繰り返し不可の読み取りの危険性が残るだけです- これらの場合、ファントム読み取りは不可能です)。これが、UPDATEがINSERT-DELETEとは異なる方法で処理され、結果として生じる異常の名前も異なる理由です。

また、単にUPDATESを処理するだけでなく、INSERT-DELETEの処理に関連する追加の処理コストもあります。


さまざまな分離レベルの利点

  • READ_UNCOMMITTEDは何も防止しません。ゼロ分離レベルです
  • READ_COMMITTEDは、1つだけ、つまりダーティリードを防ぎます。
  • REPEATABLE_READは、ダーティリードと繰り返し不可の読み取りの2つの異常を防止します。
  • SERIALIZABLEは、ダーティリード、繰り返し不可のリード、ファントムリードの3つの異常をすべて防止します。

次に、トランザクションを常にSERIALIZABLEに設定しないのはなぜですか?さて、上記の質問への答えは次のとおりです:SERIALIZABLE設定はトランザクションを非常に遅くしますが、これは望ましくありません。

実際、トランザクション時間の消費は次の割合です。

SERIALIZABLE > REPEATABLE_READ > READ_COMMITTED > READ_UNCOMMITTED

したがって、READ_UNCOMMITTED設定が最速です。


概要

実際には、ユースケースを分析して分離レベルを決定し、トランザクション時間を最適化してほとんどの異常を防ぐ必要があります。

データベースにはデフォルトでREPEATABLE_READ設定があることに注意してください。


1
UPDATEまたはDELETEは両方とも、繰り返し不可の読み取りで発生する可能性がありますか、それともUPDATEのみですか?
Dipon Roy

1
UPDATEまたはDELETEはどちらも、繰り返し
不可の

実際、同じデータベース上の別のトランザクションによって実行されるランダムなDELETEステートメントは、平均して、現在のトランザクションに対して反復不能な読み取りを引き起こす可能性が非常に低いと要約できます。ただし、同じ削除ステートメントでは、現在のトランザクションでファントム読み取りが発生する可能性が100%あります。そのように見ると、一言で言えば私の文章は少し間違っています。しかし、ねえ、私は読者に物事をより明確にするためにこのように意図的にそれを書きました。
Subhadeep Ray

+1で、シンプルでわかりやすい説明。ただし、ほとんどのデータベース(oracle、mysql)のデフォルトの分離レベルはRead Committedであり、おそらくpostgressはデフォルトのrepeatable_readを使用しています
akila

7

これら2種類の分離レベルの実装には違いがあります。
「繰り返し不可の読み取り」の場合、行ロックが必要です。
「ファントムリード」の場合、スコープ付きロックが必要です。テーブルロックも必要です。2つのフェーズロックプロトコル
を使用して、これら2つのレベルを実装できます。


反復可能読み取りまたは直列化可能を実装するために、行ロックを使用する必要はありません。
a_horse_with_no_name 2017

5

繰り返し不可の読み取りが行われるシステムでは、トランザクションAの2番目のクエリの結果にトランザクションBの更新が反映され、新しい量が表示されます。

ファントム読み取りを許可するシステムで、トランザクションBがID = 1の新しい行を挿入すると、トランザクションAは2番目のクエリが実行されたときに新しい行を参照します。つまり、ファントム読み取りは、反復不能読み取りの特殊なケースです。


ファントムリードの説明が正しいとは思いません。コミットされていないデータが表示されない場合でも、ファントム読み取りを取得できます。Wikipediaの例を参照してください(上記のコメントでリンクされています)。
Thilo

1

受け入れられた答えは、ほとんどの場合、2つの間のいわゆる区別は実際にはまったく重要ではないことを示しています。

「行が2回取得され、行内の値が読み取り間で異なる」場合、それらは同じ行ではなく(正しいRDBでは同じタプルではありません)、定義により、「コレクション2番目のクエリによって返される行は最初のクエリとは異なります。」

「どの分離レベルを使用する必要があるか」という質問に関しては、データが誰かにとって、どこかで非常に重要であるほど、Serializableが唯一の合理的なオプションであるケースが多くなります。


0

Non-repeateable-readとphantom-readにはいくつかの違いがあると思います。

Non-repeateableは、2つのトランザクションAとBがあることを意味します。BがAの変更に気付いた場合、ダーティリードが発生する可能性があるため、Aのコミット後にBにAの変更を通知させます。

新しい問題があります:Aのコミット後にBにAの変更を通知します。つまり、AがBが保持している行の値を変更します。Bが行を再度読み取る場合があるため、Bは初めて新しい値を取得します取得、私たちはそれを非反復可能と呼び、問題に対処するために、Bが開始したときにBに何かを記憶させます(何が記憶されるのかまだわからないため)。

新しいソリューションについて考えてみましょう。新しい問題もあることに気づきます。Bに何かを記憶させるため、Aで起こったことは何でも、Bは影響を受けませんが、BがテーブルとBにデータを挿入したい場合テーブルをチェックしてレコードがないことを確認しますが、このデータはAによって挿入されているため、エラーが発生する可能性があります。これをファントムリードと呼びます。


0

非反復可能読み取りは分離レベルであり、ファントム読み取り(他のトランザクションによるコミットされた値の読み取り)は概念(読み取りのタイプ、たとえばダーティー読み取りまたはスナップショット読み取り)です。反復不可能な読み取り分離レベルでは、ファントム読み取りは許可されますが、ダーティ読み取りまたはスナップショット読み取りは許可されません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.