T-SQL es una consulta sub para una restricción de actualización Atómica con la actualización?

StackOverflow https://stackoverflow.com/questions/4097126

Pregunta

Tengo una sencilla aplicación de cola en MS SQL Server 2008 R2. Aquí está la esencia de la cola:

CREATE TABLE ToBeProcessed 
(
    Id BIGINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
    [Priority] INT DEFAULT(100) NOT NULL,
    IsBeingProcessed BIT default (0) NOT NULL,
    SomeData nvarchar(MAX) NOT null
)

Quiero seleccionar atómicamente la parte superior n filas ordenadas por la prioridad y el id donde IsBeingProcessed es falsa y actualizar aquellas filas que decir que se están procesando. Pensé que haría uso de una combinación de actualización, principal, la impresión y el orden, pero por desgracia no se puede utilizar la parte superior y el orden en una instrucción de actualización.

Así que he hecho una en la cláusula para restringir la actualización y que consulta sub hace lo ordenado por (véase más adelante). Mi pregunta es, ¿toda esta declaración atómica, o necesito para envolverlo en una transacción?

DECLARE @numberToProcess INT = 2

CREATE TABLE #IdsToProcess
(
    Id BIGINT NOT null
)

UPDATE 
    ToBeProcessed
SET
    ToBeProcessed.IsBeingProcessed = 1
OUTPUT 
    INSERTED.Id 
INTO
    #IdsToProcess   
WHERE
    ToBeProcessed.Id IN 
    (
        SELECT TOP(@numberToProcess) 
            ToBeProcessed.Id 
        FROM 
            ToBeProcessed 
        WHERE
            ToBeProcessed.IsBeingProcessed = 0
        ORDER BY 
            ToBeProcessed.Id, 
            ToBeProcessed.Priority DESC)

SELECT 
    *
FROM 
    #IdsToProcess

DROP TABLE #IdsToProcess

Aquí hay algo de SQL para insertar algunas filas ficticias:

INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
INSERT INTO ToBeProcessed (SomeData) VALUES (N'');
¿Fue útil?

Solución

Si entiendo el motivo de la pregunta se quiere evitar la posibilidad de que dos transacciones simultáneas podrían ejecutar tanto la consulta sub para obtener la parte superior de N filas, para procesar a continuación, proceder a la actualización de las mismas filas?

En ese caso, me gustaría usar este enfoque.

;WITH cte As
(
SELECT TOP(@numberToProcess) 
            *
        FROM 
            ToBeProcessed WITH(UPDLOCK,ROWLOCK,READPAST) 
        WHERE
            ToBeProcessed.IsBeingProcessed = 0
        ORDER BY 
            ToBeProcessed.Id, 
            ToBeProcessed.Priority DESC
)            
UPDATE 
    cte
SET
    IsBeingProcessed = 1
OUTPUT 
    INSERTED.Id 
INTO
    #IdsToProcess  

Yo era un poco más temprano incierto si SQL Server tomaría cerraduras U al procesar su versión con la sub consulta bloqueando así dos transacciones simultáneas de la lectura de las mismas filas TOP N. Este no parece ser el caso.

tabla de prueba

CREATE TABLE JobsToProcess
(
priority INT IDENTITY(1,1),
isprocessed BIT ,
number INT
)

INSERT INTO JobsToProcess
SELECT TOP (1000000) 0,0
FROM master..spt_values v1, master..spt_values v2

Test Script (Ejecutar en 2 sesiones concurrentes SSMS)

BEGIN TRY
DECLARE @FinishedMessage VARBINARY (128) = CAST('TestFinished' AS  VARBINARY (128))
DECLARE @SynchMessage VARBINARY (128) = CAST('TestSynchronising' AS  VARBINARY (128))
SET CONTEXT_INFO @SynchMessage

DECLARE @OtherSpid int

WHILE(@OtherSpid IS NULL)
SELECT @OtherSpid=spid 
FROM sys.sysprocesses 
WHERE context_info=@SynchMessage and spid<>@@SPID

SELECT @OtherSpid


DECLARE @increment INT = @@spid
DECLARE @number INT = @increment

WHILE (@number = @increment AND NOT EXISTS(SELECT * FROM sys.sysprocesses WHERE context_info=@FinishedMessage))
UPDATE JobsToProcess 
SET @number=number +=@increment,isprocessed=1
WHERE priority = (SELECT TOP 1 priority 
                   FROM JobsToProcess 
                   WHERE isprocessed=0 
                   ORDER BY priority DESC)

SELECT * 
FROM JobsToProcess 
WHERE number not in (0,@OtherSpid,@@spid)
SET CONTEXT_INFO @FinishedMessage
END TRY
BEGIN CATCH
SET CONTEXT_INFO @FinishedMessage
SELECT ERROR_MESSAGE(), ERROR_NUMBER()
END CATCH

Casi inmediatamente la ejecución se detiene como actualizar ambas operaciones simultáneas de la misma fila por lo que las cerraduras S tomadas mientras que la identificación de la TOP 1 priority deben ser liberado antes de que aquires una cerradura U entonces los 2 transacciones proceden a obtener la U fila y bloqueo X en secuencia.

Heap

Si un IC se añade a continuación ALTER TABLE JobsToProcess ADD PRIMARY KEY CLUSTERED (priority) interbloqueo se produce casi inmediatamente en cambio, como en este caso el bloqueo de registro S no quede en libertad, una transacción aquires una cerradura U en la fila y espera para convertirlo en un bloqueo X y el otro transacción aún está esperando para convertir su bloqueo S a una cerradura U.

índice agrupado

Si la consulta se cambia de arriba para uso MIN en lugar de TOP

WHERE priority = (SELECT MIN(priority)
                   FROM JobsToProcess 
                   WHERE isprocessed=0 
                   )

A continuación, SQL Server administra para eliminar por completo la consulta sub del plan y toma U encierra todo el camino.

introducir descripción de la imagen aquí

Otros consejos

Cada instrucción T-SQL individuo es, de acuerdo con toda mi experiencia y todo el Documenation que he leído, se supone que atómica. Lo que tienes hay una sola instrucción de T-SQL, ergo es debe ser atómica y no requerirá declaraciones explícitas de transacción. He utilizado este tipo precisa de la lógica muchas veces, y nunca he tenido un problema con él. Miro adelante a ver si alguien como una opinión alternativa soportable.

A propósito, mirada en las funciones de clasificación, específicamente row_number (), para la recuperación de un número determinado de artículos. La sintaxis es quizás un poco torpe, pero en general son herramientas flexibles y potentes. (Hay alrededor de una pila bazillion preguntas y respuestas Overlow que ellos discuten.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top