2010-09-21 17 views
5

Digamos que tengo una tabla de base de datos de SQL Server con X (> 1,000,000) registros que deben procesarse (obtener datos, realizar acciones externas, actualizar el estado en db) uno a uno por algunos procesos de trabajo (ya sean aplicaciones de consola, servicio de Windows, roles de trabajador de Azure, etc.). Necesito garantizar que cada fila solo se procesa una vez. Idealmente, se garantizaría la exclusividad sin importar cuántas máquinas/procesos se formaron para procesar los mensajes. Lo que más me preocupa es que dos SELECT agarren las mismas filas al mismo tiempo.¿Cómo podría diseñar este sistema de procesamiento de mensajes en .NET/SQL Server?

Sé que hay mejores almacenes de datos para hacer cola, pero no tengo tanto lujo para este proyecto. Tengo ideas para lograr esto, pero estoy buscando más.

Respuesta

7

Tuve esta situación.

Añadir una columna InProcess a la mesa, por defecto = 0. En el proceso de consumo:

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

Ahora que la máquina posee la fila, y se puede consultar sus datos sin miedo. Por lo general, su siguiente línea será algo como esto:

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

También tendrá que añadir una bandera Done de algún tipo a la fila, lo que puede indicar si la fila fue reivindicado pero el proceso estaba incompleto.

Editar

El UPDATE obtiene un bloqueo exclusivo (ver MSDN). No estoy seguro si el SELECT en la subconsulta se puede dividir desde UPDATE; si es así, tendrías que ponerlos en una transacción.

mensajes @Will A un enlace que sugiere que a partir de su lote con esto garantizará que:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

... pero yo no lo he probado.

El enlace de @Martin Smith también hace algunos buenos comentarios, mirando la cláusula OUTPUT (agregada en SQL 2005).

Una última edición

intercambio muy interesante en los comentarios, que sin duda aprendió algunas cosas aquí. Y para eso está SO, ¿verdad?

Sólo para el color: cuando utilicé este enfoque en 2004, tenía un grupo de rastreadores web que volcaban URLs para buscar en una tabla, y luego tiraban de su siguiente URL a rastrear desde esa misma tabla. Como los rastreadores intentaban atraer malware, podían colapsar en cualquier momento.

+0

+1 Necesita una limpieza fuera de banda para el caso en el que la aplicación del consumidor no realiza la transición correcta entre "InProcess" y "Hecho" –

+0

Cualquier motivo por el que @myMachineID no puede ser @@ SPID, por supuesto, asumiendo que ambas consultas se ejecutan en el mismo lote? –

+0

¿ACTUALIZA la ACTUALIZACIÓN las filas seleccionadas mientras las actualiza o podrían varios procesos reclamar las filas simultáneamente? –

0

Consideraría tener el proceso de obtener el número N superior de registros cuyo indicador "procesado" es cero en una colección local. De hecho, tendría tres valores para la bandera procesada: NotProcessed (0), Processing (2), Processed (1). A continuación, bucle a través de su colección y ejecute el siguiente SQL:

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

... de esa manera, si algún otro proceso que ha agarrado ID de registro ya, entonces no va a establecer el campo procesado a 2.Deberá verificar que el registro ID 123456 esté realmente configurado en 2:

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

... luego puede procesarlo. Si el recuento devuelto es cero, pasará al siguiente registro de su colección y lo volverá a intentar. Si llega al final de su colección y algún otro proceso ya modificó todos esos registros, busque N registros más.

Cuestiones relacionadas