тупик на параллельных процессах, разделенных уровнем изоляции - PullRequest
1 голос
/ 23 августа 2011

У нас есть рабочий стол. Задания агента сервера собирают 100 записей из этой таблицы в курсоре и выполняют некоторую работу. Чтобы распараллелить это, есть 10 заданий агента сервера, которые вызывают следующую процедуру (каждая со своей собственной @process_id):

CREATE PROCEDURE sp_do_workorder @process_id INT
AS
BEGIN TRY
    DECLARE @wo_id NCHAR(40),
        @wo_action NVARCHAR(100),
        @created_at DATETIME,
        @source_proc_name NVARCHAR(100),

    UPDATE procedure_ctrl SET [status]='running' WHERE [procedure]='sp_do_workorder_'+CAST(@process_id AS NVARCHAR(100)) AND [status]='idle'

    WHILE 1=1
    BEGIN
        IF NOT EXISTS (SELECT * FROM procedure_ctrl WHERE [procedure]='sp_do_workorder_'+CAST(@process_id AS NVARCHAR(100)) AND [status]='running') BREAK

        SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
        BEGIN TRANSACTION
            UPDATE workorder SET hash=CAST(@process_id AS NVARCHAR(100))
            FROM workorder x
            INNER JOIN (
                SELECT TOP 100 id FROM workorder WHERE hash='' AND workorder_step=0 ORDER BY created_at ASC
            ) y ON x.id=y.id
        COMMIT TRANSACTION
        SET TRANSACTION ISOLATION LEVEL READ COMMITTED

        DECLARE wo_cur CURSOR FAST_FORWARD FOR SELECT id,action,created_at,optin_source FROM workorder WHERE hash=CAST(@process_id AS NVARCHAR(100)) AND workorder_step=0 ORDER BY created_at ASC
        OPEN wo_cur
        FETCH NEXT FROM wo_cur INTO @wo_id,@wo_action,@created_at,@source_proc_name
        WHILE @@FETCH_STATUS=0
        BEGIN
            EXEC sp_basisprozess @wo_id,@wo_action,@created_at,@source_proc_name,@process_id
            FETCH NEXT FROM wo_cur INTO @wo_id,@wo_action,@created_at,@source_proc_name
        END
        CLOSE wo_cur
        DEALLOCATE wo_cur

        WAITFOR DELAY '00:00:01'
    END

    UPDATE procedure_ctrl SET [status]='idle' WHERE [procedure]='sp_do_workorder_'+CAST(@process_id AS NVARCHAR(100)) AND [status]='running'
END TRY
BEGIN CATCH
    EXEC dbo.sp_listerror
    DECLARE @error NVARCHAR(4000)
    SET @error='[sp_do_workorder]_'+CAST(@process_id AS NVARCHAR(100))+': critical problem'
    RAISERROR(@error, 12, 1)    
END CATCH

Мы часто заходим в тупик для большинства из этих 10 рабочих мест агентов. Кто-нибудь намекнул, почему это так? Чтобы предотвратить побочные эффекты, мы используем сериализацию уровня изоляции транзакций, поэтому только одно задание агента может захватить одну запись рабочего заказа. Без установки изоляции транзакции leven тупик исчезает, но часто случается, что два (или более) задания агента захватывают одну и ту же запись рабочего задания.

1 Ответ

2 голосов
/ 10 октября 2011

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

...