UPDLOCKによりSELECTがハング(ロック)するのはなぜですか?


13

テーブル全体をロックするSQL SERVERにselectがあります。

セットアップスクリプトは次のとおりです(上書きしないようにしてください)。

USE [master]
GO

IF EXISTS(SELECT 1 FROM sys.databases d WHERE d.name = 'LockingTestDB')
DROP DATABASE LockingTestDB
GO

CREATE DATABASE LockingTestDB
GO

USE [LockingTestDB]
GO
IF EXISTS(SELECT 1 FROM sys.tables t WHERE t.name = 'LockingTestTable')
  DROP TABLE LockingTestTable
GO

CREATE TABLE LockingTestTable (
  Id int IDENTITY(1, 1),
  Name varchar(100),
  PRIMARY KEY CLUSTERED (Id)
)
GO

INSERT INTO LockingTestTable(Name) VALUES ('1')
INSERT INTO LockingTestTable(Name) VALUES ('2')
GO

新しいクエリウィンドウを開き、次のトランザクションを実行します(トランザクションには待機があります)。

USE [LockingTestDB]
GO

BEGIN TRANSACTION
  SELECT * FROM LockingTestTable t WITH (UPDLOCK, ROWLOCK) WHERE t.Name = '1'
  WAITFOR DELAY '00:01:00'

COMMIT TRANSACTION
--ROLLBACK
GO

USE [master]
GO

そして、実行される別のもの(それらが同時に実行されることを確認してください):

USE [LockingTestDB]
GO

SELECT * FROM LockingTestTable t WITH (UPDLOCK, ROWLOCK) WHERE t.Name = '2'

USE [master]
GO

2番目のクエリが最初のクエリによってブロックされることに気付くでしょう。最初のクエリを停止してROLLBACKを実行すると、2番目のクエリが完了します。

なんでこんなことが起こっているの?

PS:Nameに非クラスター化インデックス(フルカバレッジ)を追加すると修正されます:

USE [LockingTestDB]
GO

CREATE NONCLUSTERED INDEX [IX_Name] ON [dbo].[LockingTestTable] 
(
  [Name] ASC
)
INCLUDE ( [Id]) WITH (STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

なぜ再び?

回答:


19

以下のようBooks Onlineの文書化UPDLOCK更新ロックを取り、トランザクションの最後にそれらを保持しています。

ロックする行を見つけるためのインデックスがない場合、テストされたすべての行がロックされ、トランザクションが完了するまで、条件を満たす行のロックが保持されます。

最初のトランザクションは、name = 1の行で更新ロックを保持します。2番目のトランザクションは、同じ行で更新ロックを取得しようとするとブロックされます(その行のname = 2をテストするため)。

インデックスを使用すると、SQL Serverは対象となる行のみをすばやく見つけてロックできるため、競合は発生しません。

ロックヒントの理由を検証し、適切なインデックスが存在することを確認するには、資格のあるデータベースの専門家とコードを確認する必要があります。

関連情報:Read Committed Snapshot Isolationでのデータ変更

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