2009-11-30 3 views
6

Estoy escribiendo un servicio en segundo plano que necesita procesar una serie de trabajos, almacenados como registros en una tabla sqlserver. El servicio necesita encontrar los 20 trabajos más antiguos que se deben trabajar (where status = 'new'), marcarlos (set status = 'processing'), ejecutarlos y actualizar los trabajos posteriormente.Marcar atómica y devolver un grupo de filas en la base de datos

Es la primera parte con la que necesito ayuda. Podría haber varios subprocesos accediendo a la base de datos al mismo tiempo, y quiero asegurarme de que la consulta "mark & return" se ejecute atómicamente, o casi. Este servicio pasará relativamente poco tiempo accediendo a la base de datos, y no es el fin del mundo si un trabajo se ejecuta dos veces, así que podría aceptar una pequeña probabilidad de que los trabajos se ejecuten más de una vez para mayor simplicidad en el código.

¿Cuál es la mejor manera de hacerlo? Estoy usando linq-to-sql para mi capa de datos, pero supongo que tendré que bajar a t-sql para esto.

Respuesta

10

Su mesa de trabajos es una cola. La escritura de colas de copia de seguridad de tablas de usuario es notoriamente propensa a errores, ya que conduce a problemas de deadlocks y concurencia.

Lo más simple sería soltar la tabla de usuario y utilizar un verdadero queue en su lugar. Esto le dará una cola libre de concilio libre de interbloqueo en la base de código probado y validado del sistema. El problema es que todo el paradigma alrededor de las colas cambia de INSERT y DELETE/UPDATE a SEND/RECEIVE. Por otro lado, con la cola incorporada obtienes algunos objetos gratuitos muy potentes, como Activation y correlated items locking.

Si desea continuar por el camino de la tabla de usuario colas respaldados entonces el segundo truco más importante en la escritura colas de tablas de usuario es utilizar la actualización ... SALIDA:

WITH cte AS (
    SELECT TOP(20) status, id, ... 
    FROM table WITH (ROWLOCK, READPAST, UPDLOCK) 
    WHERE status = 'new' 
    ORDER BY enqueue_time) 
UPDATE cte 
    SET status = 'processing' 
OUTPUT 
    INSERTED.id, ... 

La sintaxis es CTE solo por la conveniencia de colocar TOP y ORDER BY correctamente, la consulta se puede escribir usando tablas derivadas igual que esily. No puedes usar UPDATE directo ...ARRIBA porque UPDATE no admite un ORDER BY y usted requiere que esto satisfaga la parte 'más antigua' de su requerimiento. Las sugerencias de bloqueo son necesarias para facilitar una alta concurencia entre subprocesos de procesamiento paralelos.

Dije que este es el segundo truco más importante. Lo más importante es cómo organizas la mesa. Para una cola es debe agruparse por (status, enqueue_time). Si no organiza la mesa correctamente, terminará con puntos muertos. Comentario preventivo: la fragmentación es importante en este escenario.

+0

¿Puede explicar por qué habrá interbloqueos si la tabla no está agrupada por (estado, tiempo de enqueue) incluso después de utilizar las 3 sugerencias que prescribe? –

+0

No sabía acerca de la cláusula OUTPUT, que junto con los consejos hace una solución completa. Esto responde mi propia pregunta en SO. –

8

Por favor, mira mi respuesta aquí: SQL Server Process Queue Race Condition que también gestiona 20 filas de una vez.

Básicamente, es bastante simple en SQL Server administrar la concurrencia y el sondeo usando las sugerencias ROWLOCK, READPAST y UPDLOCK.

que no puedo comentar sobre LINQ, pero una transacción todavía te deja abierto a problemas de concurrencia: es necesario utilizar los consejos que he mencionado

+0

Sus otros artículos fueron muy útiles. Me faltaba uno de los tres consejos. –

1

Sé que es fuera de tema, pero para ello se puede utilizar MSMQ. Una cola de mensajes pondría sus trabajos en secuencia y es seguro para subprocesos. También puede asignar prioridad para que MSMQ se administre solo. Puede usar leer o mirar para borrar un mensaje de la cola o simplemente ver qué hay allí. Puede usar el patrón de diseño del comando para ayudarlo con esto.

+0

Hacer cola es la respuesta, pero ¿por qué MSMQ cuando SQL Server viene con colas integradas? –

+0

La manera en que los uso es para controlar los procesos. Cuando hago cola, no uso la base de datos. Entonces cualquier listador puede conseguir un trabajo que hacer. Y lo probé con 5 computadoras con 10 procesos cada una y nunca tuve un problema de simultaneidad. Supongo que depende de dónde quieras que esperes tu cola. –

0

¿No es tan simple como ejecutar tu T-SQL dentro de una transacción, o me falta algo?

4

Basándose en gbn's answer ...

Si está utilizando SQL Server 2005 o posterior, puede devolver los registros actualizados de forma atómica utilizando un OUTPUT clause en su UPDATE declaración:

UPDATE TOP (20) your_table 
SET status = 'processing' 
OUTPUT INSERTED.* 
FROM your_table WITH (ROWLOCK, READPAST, UPDLOCK) 
WHERE status = 'new' 
Cuestiones relacionadas