Как проверить строки заблокированы при обновлении в SQL Server - PullRequest
0 голосов
/ 06 июля 2019

Каков наилучший способ проверить, блокируются ли строки во время их обновления?

У меня есть запрос, который выбирает верхние x записей таблицы, и он обновляет их, но несколько рабочих потоков будут вызывать один и тот же запрос, поэтому я хочу убедиться, что он заблокирован, и если он заблокирован, он выдаст ошибку какого-то рода, так что я могу справиться с этим соответствующим образом, но я не могу выдать исключение, которое я могу поймать в .NET (или в этом отношении в SQL).

Запрос выглядит так:

BEGIN TRANSACTION

UPDATE MyTable WITH (ROWLOCK)
SET x = @X,
    y = @Y,
WHERE ID IN (SELECT TOP 50 ID 
             FROM MyTable
             WHERE Z IS NULL)

SELECT TOP 50 x 
FROM MyTable
WHERE x = @W

COMMIT TRANSACTION

Я попытался пройти через отладчик в SQL, чтобы просто вызвать BEGIN TRANSACTION, а затем вызвать тот же запрос из моего приложения .NET и ожидал ошибки, но он просто нормально работал в .NET

Итак, мой вопрос: как я могу сгенерировать ошибку, чтобы я знал, что записи в настоящее время обновляются? Я хочу предпринять конкретное действие, когда это происходит, например, повторите попытку за x миллисекунд.

Спасибо.

1 Ответ

0 голосов
/ 06 июля 2019

Исходя из ваших последних комментариев (пожалуйста, добавьте эту информацию в ваше тело вопроса), вы просто хотите убедиться, что каждый поток «получает» только те строки, которые не доступны другим потокам.Как и выбор задач из таблицы ожидающих задач, и проверка того, что ни одна задача не будет подхвачена двумя потоками.

Вы слишком задумывались о блокировке.Ваша проблема не в том, что нужно возиться с механизмом блокировки 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 снова).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...