Как бы вы разработали эту систему обработки сообщений в .NET / SQL Server? - PullRequest
5 голосов
/ 22 сентября 2010

Допустим, у меня есть таблица базы данных SQL Server с X (> 1 000 000) записями в ней, которые должны обрабатываться (получать данные, выполнять внешние действия, обновлять состояние в БД) по очереди некоторыми рабочими процессами.(консольные приложения, служба Windows, рабочие роли Azure и т. д.).Я должен гарантировать, что каждая строка обрабатывается только один раз.В идеале исключительность должна быть гарантирована независимо от того, сколько машин / процессов было запущено для обработки сообщений.Меня больше всего беспокоит то, что два SELECT захватывают одни и те же строки одновременно.

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

Ответы [ 2 ]

7 голосов
/ 22 сентября 2010

У меня была такая ситуация.

Добавить в таблицу столбец InProcess, по умолчанию = 0. В потребительском процессе:

UPDATE tbl SET Inprocess = @myMachineID WHERE rowID = 
    (SELECT MIN(rowID) WHERE InProcess = 0)

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

SELECT * FROM tbl WHERE rowID = 
    (SELECT MAX(rowID) FROM tbl WHERE ProcessID = @myMachineID)

Вам также нужно будет добавить в строку какой-нибудь флаг Done, чтобы вы могли определить, была ли заявлена ​​строка, но обработка была неполной.

Редактировать

UPDATE получает эксклюзивную блокировку (см. MSDN ). Я не уверен, разрешено ли разделение SELECT в подзапросе от UPDATE; если это так, вам придется поместить их в транзакцию.

@ Will A публикует ссылку, которая предполагает, что начало партии с этим будет гарантировать:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

... но я не пробовал.

@ Ссылка Мартина Смита также дает хорошие результаты, если взглянуть на предложение OUTPUT (добавлено в SQL 2005).

Последнее редактирование

Очень интересный обмен в комментариях, я определенно узнал здесь несколько вещей. И вот для чего ТАК, верно?

Только для цвета: когда я использовал этот подход еще в 2004 году, у меня было несколько веб-сканеров, которые сбрасывали URL-адреса для поиска в таблицу, а затем извлекали следующий URL-адрес для сканирования из этой же таблицы. Поскольку сканеры пытались привлечь вредоносное ПО, они могли в любой момент потерпеть крах.

0 голосов
/ 22 сентября 2010

Я бы посоветовал процессу выбрать наибольшее N записей, чей флаг "обработан" равен нулю, в локальную коллекцию.На самом деле у меня есть три значения для обработанного флага: NotProcessed (0), Processing (2), Processed (1).Затем переберите вашу коллекцию и выполните следующую команду sql:

update table_of_records_to_process
set processed = 2
where record_id = 123456
and processed = 0

... таким образом, если какой-либо другой процесс уже захватил этот идентификатор записи, он не установит для обработанного поля значение 2. Вы.Я хочу убедиться, что идентификатор записи 123456 действительно установлен на 2:

select count(*)
from table_of_records_to_process
where record_id = 123456
and processed = 2

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

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