T-SQL - это дополнительное запрос для рестрикции обновления атомным обновлением?

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

Вопрос

У меня есть простая реализация очереди в MS SQL Server 2008 R2. Вот эссенкт очереди:

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
)

Я хочу аторично выбирать лучшие n строки, заказанные приоритетом и идентификатору, где iDBEAGESPROCED является ложным и обновлять эти строки, чтобы сказать, что они обрабатываются. Я думал, что буду использовать комбинацию обновления, верхнего, вывода и порядка, но, к сожалению, вы не можете использовать верх и заказывать в операторе обновления.

Итак, я сделал предложение, чтобы ограничить обновление, и этот подряд делает заказ (см. Ниже). Мой вопрос в том, это все это утверждение атомное, или мне нужно обернуть его в транзакции?

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

Вот несколько SQL, чтобы вставить некоторые фиктивные строки:

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'');
Это было полезно?

Решение

Если я понимаю мотивацию к вопросу, которую вы хотите избежать возможности того, что две одновременные транзакции могут одновременно выполнить дополнительные запросы, чтобы получить топ-строки для обработки, а затем перейти к обновлению одних и той же строки?

В этом случае я бы использовал этот подход.

;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  

Ранее я был немного неуверенным, предпримет ли SQL Server U заблокируются при обработке вашей версии с дополнительным запросом, таким образом блокируя две одновременные транзакции с чтения того же TOP N ряды. Это не похоже на случай.

Тестовая таблица

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

Тестовый скрипт (пробег 2 параллельных сеансов 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

Почти сразу же выполнение останавливается как одновременно одновременные транзакции обновляют одну и ту же строку, поэтому S Замки взяты во время идентификации TOP 1 priority должен быть выпущен до того, как он получает U заблокировать, затем 2 транзакции продолжаются, чтобы получить строку U и X Блокировка последовательно.

Heap

Если CI добавляется ALTER TABLE JobsToProcess ADD PRIMARY KEY CLUSTERED (priority) Затем тупик происходит почти сразу же, как в этом случае ряд S замок не освобождается, одна транзакция водо- U заблокировать на ряд и ждет, чтобы преобразовать его в X замок, а другая транзакция все еще ждет, чтобы преобразовать его S Блокировка до А. U замок.

Clustered Index

Если запрос выше изменен на использование MIN скорее, чем TOP

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

Затем SQL Server удается полностью устранить подряд из плана и принимает U Зафиксирует весь путь.

enter image description here

Другие советы

Каждый отдельный оператор T-SQL находится в соответствии со всем моим опытом и всем документом, который я когда-либо читал, должен быть атомным. То, что у вас есть, есть одно оператор T-SQL, ERGO должен быть атомным и не потребует явных операторов транзакций. Я использовал этот точный вид логики много раз, и никогда не имел проблем с этим. Я с нетерпением жду встречи с тем, если кто-то в качестве поддерживаемого альтернативного мнения.

Кстати, посмотрите в функции ранжирования, в частности, ROW_NUMBER (), для извлечения заданного количества элементов. Синтаксис, возможно, тапы неловко, но в целом они гибкие и мощные инструменты. (Есть о наложении базильона накладных вопросов и ответов, которые их обсуждают.)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top