17

私が行った最近のテストと読書から、XLOCKの「X」(排他的)名の部分は誤解を招くようです。実際には、UPDLOCK以上のロックはありません。それが排他的である場合、それは外部SELECTを防ぎますが、そうではありません。

読んでもテストでも、両者の違いはわかりません。

XLOCKが排他ロックを作成するのは、TABLOCKとともに使用する場合のみです。私の最初の質問は、「なぜこの粒度でのみなのか」です。

さらに、私は次のように述べているブログに出くわしました:

ただし、XLOCKヒントに注意してください。SQL ServerはXLOCKヒントを効果的に無視します!SQL Serverが、最も古いオープントランザクション以降にデータが変更されたかどうかをチェックする最適化があります。そうでない場合、xlockは無視されます。これにより、xlockヒントは基本的に役に立たなくなるため、避ける必要があります。

誰かがこの現象に遭遇しましたか?

私が見ていることに基づいて、このヒントは無視されるべきであるように思われます。

4

3 に答える 3

20

ロックとXロックの排他性U

以下のロック互換性マトリックスXでは、ロックはスキーマの安定性および挿入範囲-ヌルロックタイプとのみ互換性があることがわかります。次の追加の共有ロックタイプとU互換性がありますS/// IS/RS-SRI-SRX-S

ロック互換性マトリックスhttp://i.msdn.microsoft.com/ms186396.LockConflictTable(en-us,SQL.105).gif

Xロックの粒度

これらはすべてのレベルでうまく取り出されます。以下のスクリプトとプロファイラーのトレースは、行レベルで正常に取り出されていることを示しています。

CREATE TABLE test_table (id int identity(1,1) primary key, col char(40))

INSERT INTO test_table
SELECT NEWID() FROM sys.objects

select * from test_table with (rowlock,XLOCK) where id=10

痕跡

しかし、行はまだ読み取ることができます!

分離レベルでは、read committedSQL Serverは常にSロックを解除するとは限らず、ロックなしでコミットされていないデータを読み取るリスクがない場合は、この手順をスキップします。これは、ロックの競合が発生する保証がないことを意味します。

ただし、最初の選択がの場合with (paglock,XLOCK)、ページのロックにより、リーダーが常に必要とするページロックがブロックされるため、読み取りトランザクションが停止します。もちろん、これは並行性に影響を与えます。XIS

その他の警告

行/ページをロックしても、これはテーブル内のその行へのすべてのアクセスをブロックすることを意味するわけではありません。クラスター化インデックスの行をロックしても、対象となる非クラスター化インデックスの対応する行からデータを読み取るクエリが妨げられることはありません。

于 2011-01-05T21:27:30.263 に答える
3

これは警告ではなく、SELECTで何が起こるかについての誤解です。

ページにダーティデータが含まれていない場合、単なるSELECTは共有ロックを要求しないため、XLOCKによってブロックされません。

XLOCKによってブロックされるには、REPEATABLEREAD分離レベルで実行する必要があります。2つのことがそれを引き起こす可能性があります:

  1. INSERT / UPDATE/DELETEを介したデータの変更。更新されるテーブルは、XLOCKがオンになっているテーブルである必要はありません。
  2. トランザクション分離レベルまたはテーブルヒントを介してREPEATABLEREADを明示的に要求します。
于 2013-08-08T19:34:25.633 に答える
2

@Martinの回答のコメントに基づいて、ここに小さなスクリプトがあります(SELECTを防ぐロックをテストするには、さまざまなSSMSウィンドウでさまざまな部分を実行します。

--
--how to lock/block a SELECT as well as UPDATE/DELETE on a particular row
--

--drop table MyTable
--set up table to test with
CREATE TABLE MyTable (RowID int primary key clustered
                     ,RowValue int unique nonclustered not null) 

--populate test data
;WITH InsertData AS
(
    SELECT 4321 AS Number
    UNION ALL
    SELECT Number+1
        FROM InsertData
        WHERE Number<9322
)
INSERT MyTable
        (RowID,RowValue)
    SELECT
        Number, 98765-Number
        FROM InsertData
        ORDER BY Number
    OPTION (MAXRECURSION 5001)

-----------------------------------------------------------------------------
-- #1
--OPEN A NEW SSMS window and run this
--
--create lock to block select/insert/update/delete
DECLARE @ID int

BEGIN TRANSACTION

SELECT @ID=RowID FROM MyTable WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE RowID=6822
PRINT @ID

--COMMIT  --<<<only run the commit when you want to release the lock
          --<<<adfter opening the other new windows and running the SQL in them



-----------------------------------------------------------------------------
-- #2
--OPEN A NEW SSMS window and run this
--
--shows how a select must wait for the lock to be released
--I couldn't get SSMS to output any text while in the trnasaction, even though
--it was completing those commands (possibly buffering them?) so look at the
--time to see that the statements were executing, and the SELECT...WHERE RowID=6822
--was what was where this script is blocked and waiting
SELECT GETDATE() AS [start of run]
SELECT '1 of 2, will select row',* FROM MyTable Where RowID=6822
go
DECLARE @SumValue int
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT GETDATE() AS [before transaction, shouldn't be nuch difference]
BEGIN TRANSACTION
SELECT @SumValue=SUM(RowID) FROM MyTable WHERE ROWID<6000
SELECT GETDATE() AS [in transaction, shouldn't be much difference]
    , @SumValue AS SumValue
--everything to here will run immediately, but the select below will wait for the
-- lock to be removed
SELECT '2 of 2, will wait for lock',* FROM MyTable Where RowID=6822
SELECT GETDATE() AS [in transaction after lock was removed, should show a difference]
COMMIT


-----------------------------------------------------------------------------
-- #3
--OPEN A NEW SSMS window and run this
--
--show how an update must wait
UPDATE MyTable SET RowValue=1111 WHERE RowID=5000  --will run immediately
GO
UPDATE MyTable SET RowValue=1111 WHERE RowID=6822 --waits for the lock to be removed

-----------------------------------------------------------------------------
-- #4
--OPEN A NEW SSMS window and run this
--
--show how a delete must wait
DELETE MyTable WHERE RowID=5000 --will run immediately
go
DELETE MyTable WHERE RowID=6822  --waits for the lock to be removed
于 2011-01-07T15:20:26.487 に答える