подсказка была просто способом автора блога сказать «SQL будет блокировать это в фоновом режиме»; то есть намек просто говорит SQL, что он все равно будет делать за кулисами; поэтому подсказка не нужна.

с

В чем преимущество применения блокировок к приведенному ниже утверждению?

Точно так же, какую проблему мы бы увидели, если бы не включили эти подсказки? то есть они предотвращают состояние гонки, улучшают производительность или, может быть, что-то еще? На вопрос, возможно, они включены, чтобы предотвратить некоторые проблемы, которые я не рассматривал, а не условия гонки, которые я принял.

NB: Это переполнение от вопроса, заданного здесь:SQL Threadsafe ОБНОВЛЕНИЕ ТОП 1 для очереди FIFO

Заявление в вопросе
WITH nextRecordToProcess AS
(
    SELECT TOP(1) Id, StatusId
    FROM    DemoQueue
    WHERE   StatusId = 1 --Ready for processing
    ORDER BY DateSubmitted, Id 
)
UPDATE nextRecordToProcess
SET StatusId = 2 --Processing
OUTPUT Inserted.Id 
требованиеSQL используется для извлечения необработанной записи из очереди.Получаемая запись должна быть первой записью в очереди со статусом Готов (StatusId = 1).Может быть несколько рабочих / сеансов, обрабатывающих сообщения из этой очереди.Мы хотим, чтобы каждая запись в очереди выбиралась только один раз (то есть одним работником), и чтобы каждый работник обрабатывал сообщения в том порядке, в котором они появляются в очереди.Это нормально для одного работника работать быстрее, чем для другого (то есть, если работник A берет запись 1, тогда работник B берет запись 2, это нормально, если работник B завершает обработку записи 2 до того, как работник A завершил обработку записи 1). Мы обеспокоены только в контексте выбора записи.Там нет текущей транзакции; мы просто хотим забрать запись из очереди; нам не нужно держать его заблокированным, пока мы не вернемся, чтобы прогрессировать статус изProcessing вProcessed.Дополнительный SQL для контекста:
CREATE TABLE Statuses
(
    Id SMALLINT NOT NULL PRIMARY KEY CLUSTERED
    , Name NVARCHAR(32) NOT NULL UNIQUE
)
GO
INSERT Statuses (Id, Name)
VALUES (0,'Draft')
, (1,'Ready')
, (2,'Processing')
, (3,'Processed')
, (4,'Error')
GO
CREATE TABLE DemoQueue
(
    Id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , StatusId SMALLINT NOT NULL FOREIGN KEY REFERENCES Statuses(Id)
    , DateSubmitted DATETIME --will be null for all records with status 'Draft'
)
GO
Предлагаемое заявление

В различных блогах, обсуждающих очереди, и в вопросе, вызвавшем это обсуждение, предлагается изменить приведенное выше утверждение, включив подсказки блокировки, как показано ниже:

WITH nextRecordToProcess AS
(
    SELECT TOP(1) Id, StatusId
    FROM    DemoQueue WITH (UPDLOCK, ROWLOCK, READPAST)
    WHERE   StatusId = 1 --Ready for processing
    ORDER BY DateSubmitted, Id 
)
UPDATE nextRecordToProcess
SET StatusId = 2 --Processing
OUTPUT Inserted.Id 
Мое понимание

Я понимаю, что для блокировки требовались следующие преимущества:

UPDLOCK: поскольку мы выбираем запись для обновления ее статуса, мы должны обеспечить, чтобы любые другие сеансы, читающие эту запись после того, как мы ее прочитали, но до того, как мы обновим ее, не смогут прочитать запись с намерением обновить его (или, скорее, такой оператор должен был бы ждать, пока мы не выполним наше обновление и не снимаем блокировку, прежде чем другой сеанс сможет увидеть нашу запись с ее новым значением).ROWLOCK: пока мы блокируем запись, мы хотим убедиться, что наша блокировка влияет только на строку, которую мы блокируем; то есть, поскольку нам не нужно блокировать много ресурсов / мы не хотим влиять на другие процессы / мы хотим, чтобы другие сеансы могли читать следующий доступный элемент в очереди, даже если этот элемент находится на той же странице, что и наша заблокированная запись ,READPAST: Если другой сеанс уже читает элемент из очереди, а не ожидает, пока этот сеанс снимет блокировку, наш сеанс должен выбрать следующую доступную (не заблокированную) запись в очереди.

т. е. если бы мы запустили приведенный ниже код, я думаю, что это имело бы смысл:

DECLARE @nextRecordToProcess BIGINT

BEGIN TRANSACTION

SELECT TOP (1) @nextRecordToProcess = Id
FROM    DemoQueue WITH (UPDLOCK, ROWLOCK, READPAST)
WHERE   StatusId = 1 --Ready for processing
ORDER BY DateSubmitted, Id 

--and then in a separate statement

UPDATE DemoQueue
SET StatusId = 2 --Processing
WHERE Id = @nextRecordToProcess

COMMIT TRANSACTION

--@nextRecordToProcess is then returned either as an out parameter or by including a `select @nextRecordToProcess Id`

Однако, когда выбор и обновление происходят в одном и том же утверждении, я бы предположил, что ни один другой сеанс не может прочитать ту же запись между чтением и обновлением нашего сеанса; поэтому не было бы необходимости в явных подсказках блокировки.

Неужели я неправильно понял что-то, как работают замки; или предложение для этих подсказок связано с некоторым другим подобным, но другим вариантом использования?

Ответы на вопрос(2)

Ваш ответ на вопрос