web-dev-qa-db-ja.com

誰かがselect with nolockが更新されたデータの一部をクエリする理由を説明できますか?

私は here から回答を読んでいました(stackoverflowから、ここで尋ねるべきだと思います)

NOLOCKは、ロックをまったく配置しないことを意味します。

クエリは、単一のクエリでUPDATE前のデータの一部とUPDATE後の一部を返す場合があります。

Nolockはテーブルにロックを設定しないので、他の人が同時にクエリを実行できます。

それが示す答えと例から、データの更新中にデータをフェッチします。

なぜそれが起こるのですか?

通常の選択ではテーブルをロックしようとするので、updateステートメントを実行すると、行またはページがロックされます。次に、selectステートメントを実行しようとすると、updateステートメントのロックが解除されるまでロックをかけることができません。

しかし、この場合、selectステートメントはテーブルをロックしようとしないため、updateステートメントがロックを解放するのを待たずに実行できますか?

4
King Chan

NOLOCKがロックをまったく配置しないことを意味することは、必ずしも正しくありません。このヒントの下のクエリは、Sch-Sロックと( おそらくHOBTロック)をとります。

read committed分離レベルでは、SQL Serverは( 通常 )行レベルのSロックを取得し、データが読み取られるとすぐにそれらを解放します。これらは、コミットされていない更新で保持されているXロックと互換性がないため、ダーティリードを防ぎます。

リンクされた回答の例では、SELECTクエリは変更された行を検出してもブロックされないため、部分的な更新を読み取る可能性が高くなります。

デフォルトのread committed分離レベルでも発生する可能性がありますが、SELECTは「before」の値を持つ行と「after」の値を持つ行を読み取ります。必要なのは、

  1. 選択クエリは行R1の値を読み取り、そのSロックを解放します
  2. 更新クエリはR2を更新し、Xロックを取得します
  3. 選択クエリはR2を読み取ろうとし、ブロックされます。
  4. 更新クエリはR1を更新し、Xロックを取得します。
  5. 更新トランザクションがコミットするため、ロックが解放され、SelectがR2を読み取れるようになります

このタイプの状況は、たとえば、SELECTUPDATEが目的の行を見つけるために異なるインデックスを使用している場合に発生する可能性があります。

CREATE TABLE T
(
X INT IDENTITY PRIMARY KEY,
Y AS -X UNIQUE,
Name varchar(10),
Filler char(4000) DEFAULT 'X'
)


INSERT INTO T (Name)
SELECT TOP 2500 'A'
FROM master..spt_values

今度は1つのクエリウィンドウで

DECLARE @Sum int

SELECT 'SET @@ROWCOUNT' WHERE 1=0

WHILE (@@ROWCOUNT = 0)
SELECT @Sum = SUM(LEN(Name))
FROM T 
WHERE Y IN (-1, -2500)
HAVING SUM(LEN(Name)) = 3

これは無限ループで実行されます。別の実行で

UPDATE T 
SET Name=CASE WHEN Name = 'A' THEN 'AA' ELSE 'A' END

これにより、他のクエリのループが停止する可能性があります(そうでない場合は再試行します)。つまり、A,AAまたはAA,Aのいずれかを読み取る必要があります。

10
Martin Smith

ヒントNOLOCKは、トランザクション分離レベルREAD UNCOMMITTEDと同等で、1つのテーブルアクセスメソッドのスコープに制限されています。

コミットされていない結果セットを結果セットに表示するRUは何ですか?うーん、実際には「しないこと以下で説明します。

まあ(これはかなり単純化されていると思います)MSSQL(デフォルトの動作)はロックエンジンです。つまり、ロックを使用して、一貫した方法でデータを読み書きします。この単純化された説明では、MSSQLは2種類のロックを使用します:共有ロック排他ロック

共有(S)ロックは、リソース(行、行のページ、またはテーブル全体)の読み取りを許可しますが、リソースへの書き込みは許可しないロックです。したがって、トランザクションT1がS1ロックをR1行に置くと、R1を読み取ろうとするすべてのトランザクションがその読み取りを取得しますが、Sロックが有効な間は誰もR1に書き込むことができません。

排他(X)ロックは、共有ロックに対応するロックです。これは、リソースへの排他的アクセスを許可します-Xロックを取得したトランザクションを除いて、他のトランザクションは読み書きできません。上記の例で、T1がS1ロックではなくXロックをR1で取得した場合、T1以外の誰もそれを読み書きすることはできません。

それはテオリーです。分離レベルはロックを尊重し、その普及と特性を尊重します。 READ UNCOMMITTEDを除くすべて。これは、読み取りに関するロックに*(ここでお好みの悪口を言う)をロックに与えます。それでも、別のトランザクションがXロックを取得した行を更新することはできません。それは単に言います:"クエリプランに関連するすべてのものを読みます-その上にあるロックは無視してください。"そして、それを行います。

3
Fabricio Araujo