Как использовать подсказки блокировки, чтобы два параллельных запроса возвращали непересекающиеся результаты? - PullRequest
3 голосов
/ 25 марта 2011

У меня есть таблица SQL Tasks со столбцами Id и State. Мне нужно сделать следующее: найти любую задачу с состоянием ReadyForProcessing, получить все ее столбцы и установить ее состояние на Processing. Нечто подобное (псевдокод):

BEGIN TRANSACTION;
SELECT TOP 1 * FROM Tasks WHERE State = ReadyForProcessing
// here check if the result set is not empty and get the id, then
UPDATE Tasks SET State = Processing WHERE TaskId = RetrievedTaskId
END TRANSACTION

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

Похоже, мне нужны подсказки о блокировке. Я прочитал эту статью MSDN , но там ничего не понимаю. Как использовать подсказки блокировки для решения вышеуказанной проблемы?

Ответы [ 3 ]

2 голосов
/ 25 марта 2011

Это должно сработать.

BEGIN TRANSACTION
DECLARE @taskId
SELECT TOP (1) @taskid = TaskId FROM Tasks WITH (UPDLOCK, READPAST) WHERE State = 'ReadyForProcessing' 
UPDATE Tasks SET State = 'Processing' WHERE TaskId = @taskid
COMMIT TRAN
1 голос
/ 25 марта 2011

что-то вроде этого:

UPDATE TOP (1) Tasks 
    SET State = Processing 
    OUTPUT INSERTED.RetrievedTaskId 
    WHERE State = ReadyForProcessing 

проверить это:

DECLARE @Tasks table (RetrievedTaskId  int, State char(1))
INSERT @Tasks VALUES (1,'P')
INSERT @Tasks VALUES (2,'P')
INSERT @Tasks VALUES (3,'R')
INSERT @Tasks VALUES (4,'R')

UPDATE TOP (1) @Tasks
  SET State = 'P'
  OUTPUT INSERTED.RetrievedTaskId
  WHERE State = 'R'

SELECT * FROM @Tasks

- ВЫВОД:

RetrievedTaskId
---------------
3

(1 row(s) affected)

RetrievedTaskId State
--------------- -----
1               P
2               P
3               P
4               R

(4 row(s) affected)
0 голосов
/ 25 марта 2011

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

begin transaction;

update tasks
set state = processing
where state = readyForProcessing
and ID = (select min(ID) from tasks where state = readyForProcessing);

commit; 

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

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