Condición de carrera de la cola de procesos de SQL Server

Resuelto William Edmondson asked hace 15 años • 2 respuestas

Tengo una cola de pedidos a la que acceden varios procesadores de pedidos a través de un procedimiento almacenado. Cada procesador pasa una identificación única que se utiliza para bloquear los siguientes 20 pedidos para su propio uso. Luego, el procedimiento almacenado devuelve estos registros al procesador de pedidos para que se actúe en consecuencia.

Hay casos en los que varios procesadores pueden recuperar el mismo registro 'OrderTable' y en ese momento intentan operar en él simultáneamente. En última instancia, esto da como resultado que se produzcan errores más adelante en el proceso.

Mi próximo curso de acción es permitir que cada procesador tome todos los pedidos disponibles y simplemente realice una rotación por turnos de los procesadores, pero esperaba simplemente hacer que esta sección del código sea segura y permitir que los procesadores tomen registros cuando lo deseen.

De manera explícita: alguna idea de por qué estoy experimentando esta condición de carrera y cómo puedo resolver el problema.

BEGIN TRAN
    UPDATE  OrderTable WITH ( ROWLOCK )
    SET     ProcessorID = @PROCID
    WHERE   OrderID IN ( SELECT TOP ( 20 )
                                        OrderID
                                FROM    OrderTable WITH ( ROWLOCK )
                                WHERE   ProcessorID = 0)
COMMIT TRAN


SELECT  OrderID, ProcessorID, etc...
FROM    OrderTable
WHERE   ProcessorID = @PROCID
William Edmondson avatar Jun 02 '09 21:06 William Edmondson
Aceptado

Editar:

Busqué en Google para comprobar mi respuesta: "Procesamiento de colas de datos en SQL Server con READPAST y UPDLOCK" . Han pasado años desde que leí y jugué con esta solución.

Original:

Si utiliza la sugerencia READPAST, se omitirán las filas bloqueadas. Ha utilizado ROWLOCK, por lo que debe evitar la escalada de bloqueo. También necesitas UPDLOCK, como descubrí.

Entonces, el proceso 1 bloquea 20 filas, el proceso 2 tomará las siguientes 20, el proceso 3 toma las filas 41 a 60, etc.

La actualización también se puede escribir así:

UPDATE TOP (20)
    foo
SET
    ProcessorID = @PROCID
FROM
    OrderTable foo WITH (ROWLOCK, READPAST, UPDLOCK)
WHERE
    ProcessorID = 0

Actualizar, octubre de 2011

Esto se puede hacer de manera más elegante con la cláusula OUTPUT si necesita SELECT y UPDATE a la vez.

gbn avatar Jun 02 '2009 14:06 gbn

Puede utilizar Service Broker. También puedes usar sp_getapplock para serializar el acceso a tus filas, lo que eliminará las condiciones de carrera:

"Ayudando a la simultaneidad creando sus propios bloqueos (Mutexs en SQL)" http://sqlblogcasts.com/blogs/tonyrogerson/archive/2006/06/30/855.aspx

A-K avatar Jun 02 '2009 15:06 A-K