SELECT и UPDATE таблицы, чтобы не было перекрытия потоков - PullRequest
4 голосов
/ 21 января 2010

Скажите, у меня есть следующая таблица:

ID|Read
-------
 1|true
 2|false
 3|false
 4|false

... и мне нужно прочитать наименьший идентификатор, который имеет [Read] == false; плюс, обновите, что я сейчас прочитал.

Так что, если я выполню свою хранимую процедуру dbo.getMinID, он вернет ID: 2 и обновит [Read] -> true.

CREATE PROCEDURE [dbo].[getMinID]
(
  @QueryID INT OUTPUT 
)
BEGIN
  SELECT TOP 1 @QueryID = [ID] from Table
  UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 
END

Проблема в том, что у меня есть десять (10) асинхронных потоков, одновременно выполняющих dbo.getMinID, и я НЕ МОГУ заставить их выбрать ОДИН ИСТОЧНИК [ID] при любых обстоятельствах. Я обеспокоен тем, что между моим оператором SELECT и UPDATE выполняется второй поток, возвращая [ID]: 2 в обоих сценариях.

Как я могу убедиться, что я не выбираю / не обновляю одну и ту же запись дважды, независимо от того, сколько потоков воздействует на хранимую процедуру? ТАКЖЕ, имейте ввиду, что в таблицу ПОСТОЯННО добавлены новые строки, поэтому я не могу заблокировать таблицу!

Ответы [ 4 ]

3 голосов
/ 21 января 2010

Если вы имеете в виду параллельную блокировку типа очереди, то используйте подсказки ROWLOCK, UPDLOCK, READPAST?

Состояние гонки очереди процесса SQL Server

BEGIN TRAN

SELECT TOP 1 @QueryID = [ID] from Table WITH (ROWLOCK, UPDLOCK, READPAST)
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

COMMIT TRAN -- TRAM

Впрочем, в одном утверждении. что-то вроде

WITH T AS
(
    --ORDER BY with TOP , or perhaps MIN is better?
    SELECT TOP 1 [Read], [ID] from Table
    WITH (ROWLOCK, UPDLOCK, READPAST) ORDER BY [Read]
)
UPDATE
    T
SET
    [Read] = 1;
1 голос
/ 21 января 2010

Если вы хотите, чтобы оно было атомарным, вы должны заблокировать что-то, но это не значит, что вы должны заблокировать его на длительное время . Сначала я попытался бы выполнить несколько транзакций с ограниченной областью, но мне также было бы интересно попробовать вариант обновления, который одновременно выполняет SELECT:

UPDATE TOP (1) [foo]
SET [read] = 1
OUTPUT INSERTED.id
WHERE [read] = 0

Вы могли бы увидеть , если у этого есть какие-либо проблемы с параллелизмом - честно, я не знаю без проверки! Вам может понадобиться добавить что-то вроде WITH (ROWLOCK). Лично я хотел бы сохранить простоту и попробовать сериализованную транзакцию.

Также обратите внимание, что это не гарантирует какую запись вы получите (первую? Последнюю?)

1 голос
/ 21 января 2010

Установите уровень изоляции транзакции SERIALIZABLE и установите эксклюзивную блокировку с помощью команды SELECT:

SELECT TOP 1 @QueryID = [ID] from Table WITH (XLOCK) ORDER BY id DESC
UPDATE Table SET [Read] = 1 WHERE [ID] = @QueryID 

Это поместит XLOCK в верхний диапазон клавиш и не позволит одновременным запросам читать верхнюю запись.

Таким образом, ни одна транзакция никогда не получит одну и ту же запись.

0 голосов
/ 21 января 2010

поместите оператор select и update и оператор select в транзакцию и в начало транзакции заблокируйте таблицу, чтобы внешние потоки ожидали. С уважением, Йордан

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