2008-11-17 3 views
14

У меня есть таблица баз данных с ~ 50 тыс. Строк в ней, каждая строка представляет задание, которое необходимо выполнить. У меня есть программа, которая извлекает задание из БД, выполняет задание и возвращает результат в дБ. (эта система работает прямо сейчас)Лучший способ использовать таблицу БД в качестве очереди сообщений/заданий

Теперь я хочу разрешить нескольким задачам обработки выполнять задания, но не забудьте, что никакая задача не выполняется дважды (поскольку проблема с производительностью не в том, что это вызовет другие проблемы). Поскольку доступ путем в sproce, мой нынешний, хотя это заменить сказал sproce с чем-то, что выглядит как этот

update tbl set owner=connection_id() where avalable and owner is null limit 1; 
select stuff from tbl where owner = connection_id(); 

BTW; Задачи работника могут отбросить связь между получением задания и представлением результатов. Кроме того, я не ожидаю, что БД даже приблизится к тому, чтобы быть шеей бутылки, если я не испортил эту часть (~ 5 рабочих мест в минуту)

Есть ли проблемы с этим? Есть лучший способ сделать это?

Замечание: "Database as an IPC anti-pattern" здесь только немного, потому что 1) Я не делаю IPC (нет процесса, генерирующего строки, все они уже существуют прямо сейчас) и 2) основная проблема, описанная для этого анти-шаблона заключается в том, что он приводит к ненужной нагрузке на БД, поскольку процессы ожидают сообщений (в моем случае, если нет сообщений, все может завершаться, когда все делается)

+0

Правый - плохой = синхронный IPC с блокировкой на dbms SELECT как прочитанный. Вы, по-видимому, делаете это как стратегию введения асинхронности. – dkretz 2008-11-18 00:21:59

+0

BTW, если вы хотите поместить читателя (ы) на таймер, полезно, чтобы они проверяли нечасто, но если они находят работу, они могут слить очередь перед сном снова. – dkretz 2008-11-18 00:23:31

+0

Обратите внимание на мое редактирование: если они не найдут никакой работы, они никогда не найдут работу. Но если это не так ... – BCS 2008-11-18 00:26:23

ответ

12

Вот что я успешно использовал в прошлом:

MsgQueue схема таблицы

MsgId identity -- NOT NULL 
MsgTypeCode varchar(20) -- NOT NULL 
SourceCode varchar(20) -- process inserting the message -- NULLable 
State char(1) -- 'N'ew if queued, 'A'(ctive) if processing, 'C'ompleted, default 'N' -- NOT NULL 
CreateTime datetime -- default GETDATE() -- NOT NULL 
Msg varchar(255) -- NULLable 

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

Затем могут быть вставлены процессы 0-в-n, а процессы 0-to-n могут считывать и обрабатывать сообщения. Каждый процесс чтения обычно обрабатывает один тип сообщения. Для балансировки нагрузки может выполняться несколько экземпляров типа процесса.

Читатель извлекает одно сообщение и изменяет состояние на «А», пока оно работает. Когда это будет сделано, он изменит состояние на «C». Он может удалить сообщение или нет в зависимости от того, хотите ли вы сохранить контрольный журнал. Сообщения состояния = 'N' вытягиваются в порядке MsgType/Timestamp, поэтому есть индекс в MsgType + State + CreateTime.

Вариации:
Состояние для "E" rror.
Колонка для кода процесса чтения.
Временные метки для государственных переходов.

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


код от комментариев:

CREATE PROCEDURE GetMessage @MsgType VARCHAR(8)) 
AS 
DECLARE @MsgId INT 

BEGIN TRAN 

SELECT TOP 1 @MsgId = MsgId 
FROM MsgQueue 
WHERE MessageType = @pMessageType AND State = 'N' 
ORDER BY CreateTime 


IF @MsgId IS NOT NULL 
BEGIN 

UPDATE MsgQueue 
SET State = 'A' 
WHERE MsgId = @MsgId 

SELECT MsgId, Msg 
FROM MsgQueue 
WHERE MsgId = @MsgId 
END 
ELSE 
BEGIN 
SELECT MsgId = NULL, Msg = NULL 
END 

COMMIT TRAN 
-1

Вы пытаетесь реализовать антипаттерн «База данных как IPC» , Посмотрите, чтобы понять, почему вы должны правильно пересмотреть свое программное обеспечение.

+1

Как вы знаете, что это антипаттерн в этом случае или что дизайн программного обеспечения не подходит? У вас нет никакого контекста, на котором можно основывать этот комментарий вообще. – 2008-11-17 23:08:40

0

Вместо того, чтобы иметь владельца = null, когда он не принадлежит, вы должны установить его вместо поддельной записи. Поиск нулевого значения не ограничивает индекс, вы можете завершить сканирование таблицы. (Это для Oracle, SQL Server может отличаться)

1

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

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

Конечно, предполагается, что вы работаете с платформой Microsoft.

Смежные вопросы