Блокировать строку от чтения во время выполнения sp - PullRequest
5 голосов
/ 29 июля 2010

У меня есть много процессов .NET, которые читают сообщения из таблицы БД SQL Server 2008 и обрабатывают их по одному. Я реализую простой SP, чтобы «заблокировать» строку, читаемую каким-либо одним процессом, чтобы избежать любых двух процессов, обрабатывающих одну и ту же строку.

BEGIN TRAN

SELECT @status = status FROM t WHERE t.id = @id
IF @status = 'L'
BEGIN
    -- Already locked by another process. Skip this
    RETURN 0
END
ELSE
    UPDATE t SET status = 'L' WHERE id = @id
    RETURN 1
END

COMMIT

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

Я думаю, что это может быть решено путем реализации блока чтения каким-либо образом (т.е. сделать блок транзакций READ), но я не уверен, как это сделать. Кто-нибудь может помочь?

Заранее большое спасибо

Ryan

Ответы [ 2 ]

3 голосов
/ 29 июля 2010

Почему бы просто не заменить все это на

UPDATE t SET status = 'L' WHERE id = @id and status <> 'L'
RETURN @@ROWCOUNT

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

2 голосов
/ 29 июля 2010

Вы пробовали использовать:

SELECT @status = status FROM t (UPDLOCK) WHERE t.id = @id

Для получения более подробной информации см. эту ссылку.

Что вам не хватает, так это то, что оператор SELECT обычно не блокирует строку, поэтому, если один процесс выполнил SELECT, но еще не выполнил UPDATE, но затем приходит другой процесс и выполняет SELECT, затем он возвращает строку назад, поскольку вы ее не заблокировали.

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

EDIT Конечно, сделать это одним утверждением, как предлагает Мартин, - самый простой способ, и вам вообще не придется сталкиваться с проблемой блокировки.

...