web-dev-qa-db-ja.com

SQL Serverでは、読み取りロックはどのように機能しますか?

次の長期実行クエリがあるとします

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

上記のクエリの実行中に次のクエリが実行されたとします。

SELECT *
FROM [Table1]

最初のクエリは、最初のクエリが完了するまで2番目のクエリの実行を妨げますか?もしそうなら、最初のクエリは、2番目のクエリがすべての行、またはWHERE句に含まれる行だけで実行されるのを防ぎますか?

編集:

2番目のクエリが

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements
18
cm007

SQL Serverがクエリを実行する方法を理解する を読むことをお勧めします。これには、読み取りと書き込みがどのように機能し、ロックがどのように機能するかについての説明があります。

10000ftビューは次のようになります。

  • 読み取り演算子は、読み取るデータの共有ロックを取得しますbeforeデータを読み取ります
  • 書き込み演算子は、データを変更する前に、変更するデータの排他ロックを取得します
  • データロックは単なる文字列です。データベースおよびオブジェクトによってスコープが設定された、読み取られているキーのハッシュ。
  • ロック互換性マトリックス に従って、ロックマネージャーは許可されたすべてのロックのリストを維持し、非互換性を検出します。
  • 互換性のないリクエストは、それらをブロックする互換性のない許可が解放されるまで中断されます
  • オペレーターは、ロック階層を使用して、上位レベル(ページレベルまたはテーブルレベル、パーティションレベルのオプションを無視)でデータを読み取りまたは更新する意図を宣言します。これにより、オペレーターは個々の行をロックせずにテーブル全体をロックできます。
  • ロックの有効期間と範囲のロックは、より高い分離レベルを強制するために使用されます

これはまさに氷山の一角です。主題は広大です。あなたの例では、多くの要因に依存するため、実際にロックされているものについての質問に誰も答えることはできません。もちろん、WHERE句がなく、SELECT * FROM Table1を使用しているため、*を発行するアプリケーションはありません。これらは、とりわけ、ロック競合を正確に引き起こすため、悪い習慣です。

読み取りロックと書き込みロックが発生した場合は、行のバージョン管理とスナップショットの分離を調べる必要があります。 行のバージョン管理に基づく分離レベルについて をお読みください。

14
Remus Rusanu

編集: @ MaxVernon が指摘しているように、以下はNOLOCKを使用するための提案ではありません。トランザクションレベルをREAD UNCOMMITEDに設定することについて述べ、NOLOCKを最初に立ち上げるよりも、否定的な意味合いをそこに置きます。最初に投稿されたとおり:

迅速かつ簡単です。「はい、特定のインデックスヒントが指定されていない限り、最初のクエリは2番目のクエリをブロックします( [〜#〜] nolock [〜#〜] 、「ダーティリード」と呼ばれることもあります)または、2番目のクエリのトランザクション分離レベルがREAD UNCOMMITED(同じように動作する)に設定されている、いいえ、そうではありません。」

2番目のWITHSELECT句を含めることを伴う質問で提供された追加の詳細に応じて、相互に排他的またはその他の方法で、2つのクエリ間の相互作用はほぼ同じになります。

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

別のセッションで、以下を実行します。

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

現在保持されているロックを調べるには、sp_lockを、できればさらに別のセッションで実行します。

EXECUTE dbo.sp_lock;

KEYタイプのロックがX(排他的)モードで挿入トランザクションを実行するspidによって保持されていることを確認します。他のIX(意図的排他的)と混同しないでください。 )ロック。 lock のドキュメントは、KEYロックが範囲固有である一方で、他のトランザクションが影響を受ける列を挿入または更新して、そこに含まれるデータを変更することにより、落下する可能性があることを示しています元のクエリのその範囲内。保持されているロック自体が排他的であるため、最初のクエリはanyの他の同時トランザクションからのリソースへのアクセスを妨げています。実際には、最初のクエリで指定された範囲内にあるかどうかに関係なく、列のすべての行がロックされます。

したがって、2番目のセッションによって保持されているSロックは、WAITロックが解除されるまでXになり、別のX(またはU)が防止されます2番目のセッションが読み取り操作を完了する前に、別の並行spidからそのリソースがロックされないようにし、Sロックの存在を正当化します。

わかりやすくするために編集します。ダーティリードが ここで述べたリスク ...の簡単な説明にあると誤解しない限り、編集3:まだコミットされていないトランザクションをディスクに書き込むバックグラウンドチェックポイントの影響を考慮していないことに気付いたので、そうです、私の説明は誤解を招くものでした。

2番目のクエリでは、最初のバッチはコミットされていないデータを返すことができます(この場合、この場合)。 2番目のバッチは、デフォルトのトランザクション分離レベルREAD COMMITEDで実行され、最初のセッションでコミットまたはロールバックが完了した後にのみ戻ります。

ここから、クエリプランと関連するロックレベルを確認できますが、SQL Serverのロックに関するすべてを読むことができます here

3
Avarkx