2015-06-26 2 views
2

У меня есть пара сто линии хранимую процедуру, которая принимает один параметр (@id) и сильно упрощена, чтобы что-то вроде:Как я могу сериализовать несколько исполнений хранимой процедуры с теми же аргументами?

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

INSERT INTO #new_result 
EXEC pr_do_a_ton_of_calculations 

DELETE FROM result WHERE id = @id 

INSERT INTO result 
SELECT * FROM #new_result 

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

В идеале я хотел бы убедиться, что два соединения, выполняющие процедуру с одним и тем же параметром @id, будут выполнять как DELETE, так и INSERT серийно, без блокировки всей таблицы. Это также прекрасно, если две процедуры полностью сериализованы, если они не мешают выполнению других вызовов с другим параметром.

Есть ли способ, которым я могу это достичь?

+0

Является ли процедура вставки того же идентификатора, который она удалила? – Quassnoi

+0

Quassnoi: proc только вставляет строки с тем же «id», который был удален, но чтобы быть ясным, существует много строк с одним и тем же «id». 'Id' на самом деле является внешним ключом. –

+0

Любая конкретная причина, по которой вы используете 'READ UNCOMMITTED'? Была ли попытка справиться с этой проблемой? – Quassnoi

ответ

3

Добавить это в начале вашей хранимой процедуры:

DECLARE @lid INT 

SELECT @lid = id 
FROM result WITH (UPDLOCK, ROWLOCK) 
WHERE id = @id 

и избавиться от выше READ UNCOMMITTED.

Удостоверьтесь, что ваш id проиндексирован. Если это ссылка на другую таблицу, где она равна PRIMARY KEY, вместо этого используйте блокировку на этой таблице.

Еще лучше, используйте блокировки приложений (sp_getapplock).

+0

Кроме того, если * result * индексируется кластером с помощью 'id', то' PAGLOCK' блокирует только строки 'WHERE id = @ id' и быстрее, чем' ROWLOCK'. См. Https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx – Diego

+0

@Diego: страница может совместно использовать несколько значений 'id', что приведет к добавлению дополнительного идентификатора тоже заблокирован. – Quassnoi

+0

'id' некластеризован, и я не могу безопасно удалить изоляцию транзакции, потому что в этом процессе происходит намного больше (около 700 строк работают с другими таблицами). Есть ли ограничение на количество ресурсов, которые я могу создать с помощью 'sp_getapplock'? У меня будет несколько уникальных идентификаторов в миллионах. –

0

Вы можете использовать блокировки приложений, например:

DECLARE @ResourceName VARCHAR(200) = 'MyResource' + CONVERT(VARCHAR(20), @id) 
EXEC sp_getapplock @Resource = @ResourceName, @LockMode = 'Exclusive' 

---- Do your thing ---- 

DECLARE @ResourceName VARCHAR(200) = 'MyResource' + CONVERT(VARCHAR(20), @id) 
EXEC sp_releaseapplock @Resource = @ResourceName, @LockMode = 'Exclusive' 
0

Если вам нужны эти вещи, чтобы это произошло в гарантированном порядке, основанном на получении запроса, Service Broker будет управлять, что для вас и бросить в кучу другого выгоды тоже. Настройка этого требует некоторых действий, но «An Introduction to Asynchronous Processing with Service Broker» Джонатана Кехайяса - лучшее введение, которое я нашел. Вы должны установить «pr_do_a_ton_of_calculations» в качестве процедуры активации очереди и добавить дополнительные команды для обработки разговоров Service Broker.

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

0

Это может быть просто избавиться от ошибки и не фиксируя проблему, но

INSERT INTO result 
SELECT * 
    FROM #new_result 
    left join result 
    on #new_result.PK = result.PK 
where result.PK is null 

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

delete d 
    from result d 
    left join #new_result 
    on #new_result.PK = d.PK 
where #new_result.PK is null 
    and d.ID = @ID; 

update result 
SELECT result.colx = #new_result.colx, result.coly = #new_result.coly 
    FROM #new_result 
    join result 
    on #new_result.PK = result.PK 

INSERT INTO result 
SELECT * 
    FROM #new_result 
    left join result 
    on #new_result.PK = result.PK 
where result.PK is null 
Смежные вопросы