2008-11-17 14 views
14

Tengo una tabla de bases de datos con ~ 50K filas, cada fila representa un trabajo que debe realizarse. Tengo un programa que extrae un trabajo de la BD, hace el trabajo y devuelve el resultado a la base de datos. (este sistema se está ejecutando en este momento)La mejor manera de usar una tabla de DB como mensaje/cola de trabajos

Ahora quiero permitir que más de una tarea de procesamiento realice trabajos, pero asegúrese de que ninguna tarea se haga dos veces (ya que una preocupación de rendimiento no es que esto cause otros problemas). Debido a que el acceso es a través de un sproce, mi actual aunque es reemplazar dicho sproce con algo que se parece a esto

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

cierto; las tareas de los trabajadores pueden tener que ver con la conexión entre obtener un trabajo y enviar los resultados. Además, no espero que el DB siquiera se acerque a ser el cuello de la botella a menos que arruine esa parte (~ 5 trabajos por minuto)

¿Hay algún problema con esto? ¿Hay una mejor manera de hacer esto?

Nota: el "Database as an IPC anti-pattern" es solo un poco oportuno aquí porque 1) No estoy haciendo IPC (no hay ningún proceso que genere las filas, todos ya existen en este momento) y 2) la queja principal descrita para ese antipatrón es que es el resultado de la carga que no sean necesarios en la base de datos como procesos esperan mensajes (en mi caso, si no hay mensajes, todo puede se realiza el apagado, ya que todo)

+0

Derecha - mal = IPC síncrono con bloqueo en un dbms SELECCIONAR como lectura. Presumiblemente estás haciendo esto como una estrategia para introducir la asincronía. – dkretz

+0

Por cierto, si desea poner los lectores en un temporizador, es útil hacer que revisen con poca frecuencia, pero si encuentran trabajo, pueden drenar la cola antes de volver a dormir. – dkretz

+0

Nota mi edición: si no encuentran trabajo, nunca encontrarán trabajo. Pero si eso no fuera cierto ... – BCS

Respuesta

12

Esto es lo que he usado con éxito en el pasado:

esquema de la tabla 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 

Sus tipos de mensajes son lo que se espera - mensajes que se ajustan a un contrato entre la (s) inserción (es) del proceso y la (s) lectura (es) del proceso, estructurados con XML o su otra opción de representación (JSON sería útil en algunos casos, por ejemplo).

Luego, los procesos de 0 a n se pueden insertar y los procesos de 0 a n pueden leer y procesar los mensajes. Cada proceso de lectura generalmente maneja un tipo de mensaje único. Se pueden ejecutar varias instancias de un tipo de proceso para equilibrar la carga.

El lector extrae un mensaje y cambia el estado a "A" mientras trabaja en él. Cuando termina, cambia el estado a "C" ompleto. Puede eliminar el mensaje o no dependiendo de si desea mantener el seguimiento de auditoría. Los mensajes de estado = 'N' se extraen en orden MsgType/Timestamp, por lo que hay un índice en MsgType + State + CreateTime.

Variaciones:
Estado de "E" rror.
Código de proceso Column for Reader.
Marcas de tiempo para transiciones de estado.

Esto ha proporcionado un mecanismo agradable, escalable, visible y simple para hacer una serie de cosas como las que está describiendo. Si tiene una comprensión básica de las bases de datos, es bastante infalible y extensible.


Código de:

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 
+0

La parte descrita como "El lector extrae un mensaje y cambia el estado a" A "ctive mientras trabaja en él." es la parte que me interesa. ¿Cómo haces eso? (Aparte de eso, parece que el mío es el mismo que el tuyo sin las cosas que no son necesarias para mi caso.) – BCS

+0

Correcto, eso requiere múltiples declaraciones SQL entre BEGIN TRAN y COMMIT TRAN. Inmediatamente después - un SP para tirar del siguiente mensaje - hackeado un poco, he omitido el error de captura ya que fue escrito antes de TRY/CATCH. – dkretz

+1

- PARTE 1 CREATE PROCEDURE GetMessage @MsgType VARCHAR (8) ) AS DECLARE @MsgId INT COMENZAR TRAN SELECT TOP 1 @MsgId = MsgId DE MsgQueue DONDE MessageType = @pMessageType y el estado = 'N' ORDER BY CreateTime – dkretz

-1

Usted está tratando de poner en práctica de antipatrón "base de datos como IPC" . Búsquelo para comprender por qué debería considerar rediseñar su software correctamente.

+1

¿Cómo sabes que es un antipatrón en este caso, o que el diseño del software es incorrecto? No tiene ningún contexto sobre el cual basar este comentario en absoluto. –

+0

Google rápido no encuentra nada en "Base de datos como IPC" –

+0

Lo llamé un patrón útil para IPC asíncrono. Puede configurarlo para que funcione como cualquier cola de mensajes de variedad de jardín, y ellos no están en mi experiencia marcados como "antipatrones". – dkretz

0

En lugar de tener owner = null cuando no es de su propiedad, debe establecerlo en un registro de nadie falso en su lugar. La búsqueda de null no limita el índice, puede terminar con un escaneo de tabla. (Esto es para Oracle, SQL Server puede ser diferente)

1

Del mismo modo que un posible cambio de la tecnología, es posible considerar el uso de MSMQ o algo similar.

Cada uno de sus trabajos/subprocesos podría consultar la cola de mensajes para ver si hay un nuevo trabajo disponible. Como el acto de leer un mensaje lo elimina de la pila, se asegura de que solo un trabajo/hilo reciba el mensaje.

Por supuesto, esto es asumiendo que está trabajando con una plataforma de Microsoft.

+0

Tengo los datos en la base de datos, cuando termine necesito los datos en la base de datos. En mi caso, no veo ninguna razón para agregar otro componente al sistema. (BTW http://www.microsoft.com/windowsserver2003/technologies/msmq/default.mspx) – BCS

Cuestiones relacionadas