2

SQL Server にキューを実装しており (これについては議論しないでください)、競合状態の問題が発生しています。対象の T-SQL は次のとおりです。

set transaction isolation level serializable
begin tran
declare @RecordId int
declare @CurrentTS datetime2
set @CurrentTS=CURRENT_TIMESTAMP
select top 1 @RecordId=Id from QueuedImportJobs with (updlock) where Status=@Status and (LeaseTimeout is null or @CurrentTS>LeaseTimeout) order by Id asc
if @@ROWCOUNT> 0
begin
update QueuedImportJobs set LeaseTimeout = DATEADD(mi,5,@CurrentTS), LeaseTicket=newid() where Id=@RecordId
select * from QueuedImportJobs where Id = @RecordId
end
commit tran

RecordIdは PK で、 にもインデックスがありStatus,LeaseTimeoutます。

私が基本的に行っているのは、たまたまリースが期限切れになったレコードを選択すると同時に、リース時間を 5 分に更新し、新しいリース チケットを設定することです。

問題は、いくつかのスレッドを使用してこのコードを並行して実行すると、デッドロックが発生することです。updateステートメントが同じレコードに対して2回実行されることがあることがわかるまでデバッグしました。今、私はwith (updlock)これを防ぐべきだという印象を受けました(これはxlockではなく、btwでも起こりtablockxます)。したがって、実際には RangeS-U と RangeX-X ロックが同じ範囲のレコードにあるように見えますが、これは不可能なはずです。

それで、私は何が欠けていますか?私はそれがトップ1句と関係があるのではないかと考えていますか、それともSQL Serverはそれwhere Id=@RecordIdが実際にロックされた範囲にあることを知りませんか?

デッドロック グラフ: ここに画像の説明を入力

テーブル スキーマ (簡略化): ここに画像の説明を入力

4

2 に答える 2

1

ロックが異なる HOBT にあるようです。テーブルに複数のインデックスがありますか?

その場合、selectは1 つのインデックス with (updlock)のみをロックする可能性があります。update

于 2012-07-05T21:14:57.337 に答える
1

それだけではない理由:

DECLARE @t TABLE(Id INT);

UPDATE TOP (1) dbo.QueuedImportJobs 
  SET LeaseTimeout = DATEADD(MINUTE, 5, CURRENT_TIMESTAMP)
  OUTPUT inserted.Id INTO @t
  WHERE Status = @Status 
  AND COALESCE(LeaseTimeout, '19000101') < CURRENT_TIMESTAMP;

SELECT <cols> FROM dbo.QueuedImportJobs 
  WHERE Id IN (SELECT Id FROM @t);

余談ですがORDER BY、目的のインデックス順序に従って、選択した行がキューの最初の行であることを確認する必要がある場合があります。Id のインデックスがクラスター化されている場合は、おそらくこのように機能しますが、そう言わない限り保証はありません。ORDER BYに直接適用 (またはインデックス ヒント)できないため、これにはクエリのわずかな再構築が必要になりますUPDATE

WITH x AS
(
  SELECT TOP (1) Id, LeaseTimeout
    FROM dbo.QueuedImportJobs
    WHERE Status = @Status 
    AND COALESCE(LeaseTimeout, '19000101') < CURRENT_TIMESTAMP
    ORDER BY Id
)
UPDATE x
  SET LeaseTimeout = DATEADD(MINUTE, 5, CURRENT_TIMESTAMP)
  OUTPUT inserted.id INTO @t;
于 2012-07-05T21:19:08.480 に答える