Исходя из ваших последних комментариев (пожалуйста, добавьте эту информацию в ваше тело вопроса), вы просто хотите убедиться, что каждый поток «получает» только те строки, которые не доступны другим потокам.Как и выбор задач из таблицы ожидающих задач, и проверка того, что ни одна задача не будет подхвачена двумя потоками.
Вы слишком задумывались о блокировке.Ваша проблема не в том, что нужно возиться с механизмом блокировки SQL.Вы можете возиться с блокировкой, если вам нужно настроиться по соображениям производительности, но вы не в состоянии определить, нужно ли это.Я даже не стал бы беспокоиться о подсказках блокировки на этом этапе.
То, что вы хотите сделать, это иметь поле в строке, которое указывает, была ли строка взята и кем.Ваш подготовленный образец T-SQL не использует ничего согласованно, но самым близким является столбец Z
.
Вам нужно выбрать строки, имеющие значение NULL (еще не принято) в Z
.Вы уже на этом пути.Это поле может быть битом Да / Нет, и его можно заставить работать (посмотрите предложение OUTPUT в UPDATE, чтобы увидеть, как вы можете выбрать, какие строки были выбраны);но я подозреваю, что вы найдете гораздо более полезным для целей трассировки / отладки, чтобы иметь возможность идентифицировать строки, взятые вместе, глядя только на базу данных.Я бы использовал столбец, который может содержать уникальное значение, которое не может использоваться ни одним другим потоком одновременно.
Существует несколько подходов.Вы можете использовать идентификатор потока (клиентский процесс) или аналогичный, но это будет повторяться со временем, что может быть нежелательным.Вы можете создать SQL SEQUENCE и использовать эти значения, что имеет приятную особенность - быть инкрементным, но это делает SQL немного сложнее для понимания.Для иллюстрации я пойду с GUID.
DECLARE @BatchId AS UNIQUEIDENTIFIER
SET @BatchId = NEWID()
UPDATE MyTable
SET x = @X,
y = @Y,
Z = @BatchId
WHERE ID IN (SELECT TOP 50 ID
FROM MyTable
WHERE Z IS NULL)
Теперь только эти строки могут использовать только ваши потоки (при условии, конечно, что ни один поток не обманывает и не нарушает шаблон кода).Обратите внимание, что я даже не открыл явную транзакцию.В SQL Server UPDATE являются транзакционными атомарными по умолчанию (они выполняются внутри неявной транзакции).Никакой поток не может выбрать эти строки снова, потому что ни одному потоку (по умолчанию) не разрешено обновлять или даже видеть эти строки, пока UPDATE не будет зафиксирован полностью (для всех строк).Любой поток, который пытается запустить UPDATE одновременно, либо получает разные неназначенные строки (в зависимости от подсказок блокировки и того, насколько хитро вы выбираете строки для выбора - это часть «расширенной» настройки производительности), либо приостанавливается на несколькомиллисекунды, ожидающие завершения первого обновления.
Это все, что вам нужно.В самом деле.Строки только ваши и ваши:
SELECT @BatchId AS BatchId, x
FROM MyTable
WHERE Z = @BatchId
Теперь ваш код получает данные из своих выделенных строк плюс уникальный идентификатор, который он может использовать позже для ссылки на те же строки, если вам нужно (уберитеBatchId из возвращаемого набора, если он вам действительно не нужен).
Если мое чтение того, что вы пытаетесь сделать, является правильным, вы, вероятно, захотите пометить строки, когда ваш код будет выполнен, установивдругое поле для значения флага или отметки времени или чего-то еще.Таким образом, вы сможете определить, были ли строки «осиротевшими», потому что они были приняты для обработки, но процесс прекратился по любой причине (в этом случае их, вероятно, нужно снова сделать доступными, установив Z
в NULL снова).