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
が実際にロックされた範囲にあることを知りませんか?
デッドロック グラフ:
テーブル スキーマ (簡略化):