25

SQL Serverのトランザクション分離レベルを使用すると、ダーティリードなどの特定の不要な同時実行の問題を回避できます。

私が今興味を持っているのは更新が失われることです。2つのトランザクションが、誰にも気付かれることなく互いの更新を上書きする可能性があるという事実です。これを回避するために、少なくともどの分離レベルを選択する必要があるかについて、矛盾するステートメントを見たり聞いたりします。

Kalen Delaneyは、「SQL Server Internals」の本で次のように述べています(第10章-トランザクションと同時実行性-592ページ)。

Read Uncommitted分離では、失われた更新を除いて、前述のすべての動作が可能です。

一方、クラスを提供する独立したSQL Serverトレーナーは、更新の損失を回避するために、少なくとも「繰り返し可能な読み取り」が必要であると教えてくれました。

それで、誰が正しいのですか?なぜ??

4

6 に答える 6

19

答えるのに遅すぎるかどうかはわかりませんが、大学でのトランザクション分離レベルについて学んでいるところです。調査の一環として、次のリンクに出くわしました。

Microsoft Technet

具体的には、問題の段落は次のとおりです。

失われた更新

失われた更新は、2つの方法のいずれかで解釈できます。最初のシナリオでは、最初のトランザクションがコミットまたはロールバックされる前に、あるトランザクションによって更新されたデータが別のトランザクションによって上書きされたときに、失われた更新が発生したと見なされます。 このタイプの失われた更新は、トランザクション分離レベルでは許可されていないため、SQLServer2005では発生しません。

失われた更新のもう1つの解釈は、あるトランザクション(トランザクション#1)がデータをローカルメモリに読み込み、次に別のトランザクション(トランザクション#2)がこのデータを変更して、その変更をコミットする場合です。この後、トランザクション#1は、トランザクション#2が実行される前にメモリに読み込んだ内容に基づいて同じデータを更新します。この場合、トランザクション#2によって実行された更新は、失われた更新と見なすことができます。

つまり、本質的には両方の人が正しいのです。

個人的に(そして私は間違っていることを受け入れているので、私はこれを学んでいるので私を訂正してください)私はこれから次の2つのポイントを取ります:

  1. トランザクション環境の全体的なポイントは、上の段落で説明されているように、更新が失われるのを防ぐことです。したがって、最も基本的なトランザクションレベルでもそれができない場合は、なぜわざわざそれを使用するのでしょうか。

  2. 失われた更新について話すとき、最初の段落が適用されることを知っているので、一般的に言えば、失われた更新の2番目のタイプを意味します。

繰り返しになりますが、私もこれを理解したいので、ここで何か問題がある場合は訂正してください。

于 2012-01-11T01:28:40.587 に答える
14

この本の例は、店員Aと店員Bがウィジェットの出荷を受け取っている例です。

彼らは両方とも現在の在庫をチェックします、25が在庫にあるのを見てください。店員Aには50個のウィジェットと75個の更新があり、店員Bには20個のウィジェットがあるため、45個の更新が前の更新を上書きします。

私は彼女がこの現象を店員Aが行うことによってすべての隔離レベルで回避できることを意味したと思います

UPDATE Widgets
SET StockLevel = StockLevel + 50
WHERE ...

と店員Bがやって

UPDATE Widgets
SET StockLevel = StockLevel + 20
WHERE ...

確かに、SELECTUPDATEが別々の操作として実行される場合はrepeatable read、これを回避してS、トランザクションの期間中、行のロックが保持されるようにする必要があります(このシナリオではデッドロックが発生します)

于 2011-11-20T13:26:26.267 に答える
7

ユーザーがデータをWebページに読み込んでから更新する場合のように、読み取りと書き込みが別々のトランザクションで行われている場合でも、更新が失われる可能性があります。このような場合、特に接続が接続プールから再利用される場合、分離レベルで保護することはできません。rowversionなどの他のアプローチを使用する必要があります。 これが私の定型の答えです。

于 2011-11-21T02:17:25.450 に答える
3

私の経験では、Read Uncommitted「失われた更新」は発生しなくなりましたが、「失われたロールバック」は発生する可能性があります。SQLトレーナーはおそらくその同時実行性の問題を参照していたので、あなたが探している答えはですRepeatable Read

そうは言っても、これに反する経験を持っている人がいたら、とても興味があります。

于 2011-11-20T12:44:14.753 に答える
0

Francis Rodgersが指摘しているように、SQL Serverの実装に依存できるのは、トランザクションが一部のデータを更新すると、すべての分離レベルが常にデータに対して「更新ロック」を発行し、分離レベルに関係なく、別のトランザクションからの更新と書き込みを拒否することです。は。この種の失われた更新がカバーされていることを確認できます。

ただし、トランザクションが一部のデータを読み取る場合(Repeatable Readとは異なる分離レベル)、別のトランザクションがこのデータを変更して変更をコミットできます。最初のトランザクションが同じデータを更新する場合は、今回は、彼が作成した内部コピーに基づいて、管理システムはそれを保存するために何もできません。

そのシナリオでのあなたの答えは、最初のトランザクションで繰り返し可能な読み取りを使用するか、データに対して最初のトランザクションからの読み取りロックを使用することです(私は自信を持ってそれについて本当に知りません。私はこれの存在を知っていますロックとそれらを使用できること。これは、このアプローチに関心のある人なら誰でも役立つかもしれません。MicrosoftDesigning Transactions and Optimizing Locking)。

于 2015-03-01T00:11:36.270 に答える
0

以下はからの引用です 70-762 Developing SQL Databases (p. 212)

2つのプロセスが同じ行を読み取り、そのデータを異なる値で更新すると、別の潜在的な問題が発生する可能性があります。これは、トランザクションが最初に値を変数に読み込み、その後のステップで更新ステートメントでその変数を使用する場合に発生する可能性があります。この更新が実行されると、別のトランザクションが同じデータを更新します。これらのトランザクションのいずれかが最初にコミットされると、他のトランザクションの更新に置き換えられたため、更新が失われます。分離レベルを使用してこの動作を変更することはできませんが、失われた更新を特に許可するアプリケーションを作成することはできます。

したがって、このような場合、どの分離レベルも役に立たないようであり、コード自体で問題を解決する必要があります。例えば:

DROP TABLE IF EXISTS [dbo].[Balance];

CREATE TABLE [dbo].[Balance]
(
    [BalanceID] TINYINT IDENTITY(1,1)
   ,[Balance] MONEY
   ,CONSTRAINT [PK_Balance] PRIMARY KEY
   (
        [BalanceID]
   )
);

INSERT INTO [dbo].[Balance] ([Balance])
VALUES (100);

-- query window 1
BEGIN TRANSACTION;

    DECLARE @CurrentBalance MONEY;

    SELECT @CurrentBalance = [Balance]
    FROM [dbo].[Balance]
    WHERE [BalanceID] = 1;

    WAITFOR DELAY '00:00:05'

    UPDATE [dbo].[Balance]
    SET [Balance] = @CurrentBalance + 20
    WHERE [BalanceID] = 1;

COMMIT TRANSACTION;

-- query window 2
BEGIN TRANSACTION;

    DECLARE @CurrentBalance MONEY;

    SELECT @CurrentBalance = [Balance]
    FROM [dbo].[Balance]
    WHERE [BalanceID] = 1;

    UPDATE [dbo].[Balance]
    SET [Balance] = @CurrentBalance + 50
    WHERE [BalanceID] = 1;

COMMIT TRANSACTION;

テーブルを作成し、コードの各部分を個別のクエリウィンドウで実行します。分離レベルを変更しても何も起こりません。たとえば、との唯一の違いread committedrepeatable read、最後のトランザクションが最初のトランザクションの終了時に2番目のトランザクションをブロックし、値を上書きすることです。

于 2018-12-03T16:52:56.747 に答える