2012-06-06 16 views
7

Tengo una tabla de base de datos que contiene algunos registros para procesar. La tabla tiene una columna de bandera que representa los siguientes valores de estado. 1 - listo para ser procesado, 2 procesado con éxito, 3 procesamiento fallido.Opciones para usar subprocesamiento múltiple para procesar un grupo de registros de base de datos?

El código .net (proceso repetitivo - consola/servicio) capturará una lista de registros que están listos para ser procesados, y los recorre e intenta procesarlos (No muy largo), actualiza el estado según el éxito o fracaso.

Para tener un mejor rendimiento, quiero habilitar el multihebra para este proceso. Estoy pensando en engendrar decir 6 hilos, cada uno de los hilos agarrando un subconjunto.

Obviamente, quiero evitar que diferentes hilos procesen los mismos registros. No quiero tener una bandera "Ser procesado" en la base de datos para manejar el caso donde el hilo se cuelga dejando el registro colgando.

La única forma en que veo hacer esto es obtener la lista completa de registros disponibles y asignar un grupo (tal vez identificadores) a cada hilo. Si un hilo individual falla, sus registros no procesados ​​se recogerán la próxima vez que se ejecute el proceso.

¿Hay alguna otra alternativa a dividir los grupos antes de asignarlos a los hilos?

+0

¿Tiene alguna columna de identidad en su tabla? – YavgenyP

+1

Realmente no debería utilizar su base de datos como cola. http://en.wikipedia.org/wiki/Database-as-IPC – Oded

+0

Los subprocesos y procesos mueren de forma inesperada y en su mayoría en el peor momento. No es una buena idea vincular una ID de DB a una ID de subproceso/número de subproceso. –

Respuesta

6

El modo más sencillo de implementar este requisito es utilizar la tarea de Parallel Library

Parallel.ForEach (o Parallel.For).

Permitir que administre hilos de trabajo individuales.

Por experiencia, recomiendo lo siguiente:

  • Tener un estatuto de "procesamiento" adicional
  • tener una columna en la base de datos que indica cuando un registro fue recogido para su procesamiento y una tarea de limpieza/proceso que se ejecuta periódicamente buscando registros que han estado "Procesando" durante demasiado tiempo (restablezca el estado a "listo para el procesamiento")
  • Aunque no lo desee, "ser procesado" será esencial para la recuperación del bloqueo escenarios (a menos que pueda tolerar que el mismo registro se procese dos veces).

Alternativamente

considerar el uso de una cola transaccional (MSMQ o MQ conejo vienen a la mente). Están optimizados para este mismo problema.

Esa sería mi elección clara, habiendo hecho ambos a escala masiva.

Optimización

Si se necesita una cantidad no trivial de tiempo para recuperar datos de la base de datos, se puede considerar un/patrón Consumidor Productor, que es bastante sencillo de implementar con un BlockingCollection. Ese patrón permite que un hilo (productor) llene una cola con registros de BD para ser procesada, y múltiples otros hilos (consumidores) para procesar elementos fuera de esa cola.

una nueva alternativa

Teniendo en cuenta que varios pasos de procesamiento tocar el registro antes de que se considera completa, echar un vistazo a Windows Workflow Foundation como una posible alternativa.

+0

Para la recuperación de fallos, se requiere lo que quiere decir "procesarse". Realmente no es así, si el estado del registro no se actualizó a éxito o fracaso, se procesará nuevamente. Solo después de que se establece en éxito o en fracaso, se considera procesado. Hasta entonces puedes jugarlo una y otra vez. No quiero tener otro proceso que busque restablecer el indicador de "procesamiento", que puede no ser del todo exacto y tampoco puedo permitir el retraso a veces para que ese proceso restablezca el indicador para que se procese de nuevo. –

+0

También estoy de acuerdo MSMQ es una buena alternativa. Pero una cosa que no he mencionado aquí es que el registro pasa por muchas etapas (manejadas por diferentes procesos) antes de que se marque como correcto o fallido, por ejemplo, inicializado, movido al estado A, B, y luego éxito o falla. No estoy seguro si la cola todavía es buena para esos escenarios. Leí en alguna parte cuando se invocan flujos de trabajo como ese, db es mejor. –

+0

@AlexJ: "Solo después de establecerse como correcto o incorrecto, se considera procesado" significa que no necesita el estado adicional. Muchos sistemas no pueden permitir que los registros parcialmente procesados ​​simplemente se vuelvan a procesar sin alguna lógica de reversión. Dado que el tuyo puede tolerar eso, eres bueno para ir en ese sentido. Simplemente iría con Parallel.ForEach() e introduciría un BlockingQueue si y solo si pasa un tiempo considerable obteniendo registros del DB antes de que puedan ser procesados. –

2

Recuerdo hacer algo como lo que describiste ... Un hilo revisa de vez en cuando si hay algo nuevo en la base de datos que deba procesarse. Cargará solo los nuevos identificadores, por lo que si en el momento x la última lectura de id es 1000, en x + 1 se leerá en el id. 1001.

Todo lo que lee va a una fila segura. Cuando los elementos se agregan a esta cola, usted notifica los hilos de trabajo (tal vez use autoreset events, o spawn threads aquí). cada hilo leerá de este hilo una cola segura de un elemento a la vez, hasta que la cola se vacíe.

No debe asignar antes del trabajo foreach thread (a menos que sepa que el archivo foreach el proceso toma la misma cantidad de tiempo). si un hilo termina el trabajo, entonces debería tomar la carga de los otros que quedan. usando esta cola segura para hilos, te aseguras de esto.

+0

Si usa una cola en la memoria, no puede recuperarse de los escenarios de bloqueo a menos que sea aceptable procesar el mismo registro varias veces. –

+0

de hecho, pero si él no puede cambiar esos 3 estados? –

+0

Ah, pero él puede. Él acaba de decir que no quiere debido a la recuperación del bloqueo de hilos, pero de hecho lo necesita, para permitir la recuperación (suponiendo que las repeticiones de los registros no sean permisibles). –

0

Aquí hay un enfoque que no depende/usa una columna de base de datos adicional (pero vea el n. ° 4) ni ordena una cola en proceso. La premisa de este enfoque es "fragmentar" los registros entre los trabajadores en función de un valor consistente, muy parecido a un caché distribuido.

Éstos son mis suposiciones:

  1. Re-tratamiento no cause efectos secundarios no deseados; como mucho, algún trabajo "se desperdicia".
  2. El número de hilos se fija al inicio. Esto no es un requisito, pero simplifica la implementación y me permite omitir detalles transitorios en la descripción simple a continuación.
  3. Solo hay un "proceso de trabajo" (pero vea # 1) controlando los "hilos de trabajo". Esto simplifica tratar con la forma en que los registros se dividen entre los trabajadores.
  4. Hay alguna columna de "ID" [inmutable] que está "bien distribuida". Esto es necesario para que el trabajador de búsqueda obtenga la misma cantidad de trabajo.
  5. El trabajo se puede hacer "fuera de servicio" siempre que "finalmente se termine". Además, es posible que los trabajadores no siempre se ejecuten "al 100%" debido a que cada uno trabaja efectivamente en una cola diferente.

Asigna a cada hilo un valor bucket único desde [0, thread_count). Si un hilo muere/se reinicia tomará el mismo cubo que el que dejó vacante.

Entonces, cada vez que un hilo necesita se necesita un nuevo registro se ha podido recuperar de la base de datos:

SELECT * 
FROM record 
WHERE state = 'unprocessed' 
AND (id % $thread_count) = $bucket 
ORDER BY date 

Podría, por supuesto, otros supuestos acerca de la lectura de la "esta roscas tareas" en lotes y el almacenamiento ellos localmente Sin embargo, una cola local sería por subproceso (y, por lo tanto, se volvería a cargar con un nuevo inicio de subproceso) y, por lo tanto, solo trataría los registros asociados para el bucket dado.

Cuando el subproceso finaliza el procesamiento de un registro debe marcar el registro como procesado utilizando el nivel de aislamiento apropiado y/o concurrencia optimista y continuar con el siguiente registro.

+0

¿Por qué administrar los hilos tú mismo? El TPL es tan bueno en eso ... Además, esto requiere múltiples viajes de ida y vuelta a la base de datos, de lo contrario uno lo hará. –

+0

@EricJ. Estaba tratando de evitar especificar la implementación de los hilos. La idea de este enfoque es "fragmentar" los registros entre subprocesos de trabajo en función de un valor constante, muy parecido a un caché distribuido. –

+0

Pero especifica que la implementación * no * será Paralela.Para (Cada), ya que ambos administran automáticamente el ciclo de vida del subproceso y no alearían su segunda suposición. –

Cuestiones relacionadas