Я сталкивался с этой ситуацией в прошлом и в конечном итоге использовал SP_GetAppLock для создания семафора на ключе, чтобы предотвратить состояние гонки. Несколько лет назад я написал статью, в которой обсуждались различные методы. Статья здесь:
http://www.sqlservercentral.com/articles/Miscellaneous/2649/
Основная идея заключается в том, что вы получаете блокировку на сконструированный ключ, который отделен от таблицы. Таким образом, вы можете быть очень точными и только блокировать пауки, которые потенциально могут создать состояние гонки и не блокировать других потребителей стола.
Я оставил суть статьи ниже, но я бы применил эту технику, получив блокировку на сконструированном ключе, таком как
@Key = 'GiftCardTransaction' + GiftCardId
Получение блокировки для этого ключа (и обеспечение того, что вы последовательно применяете этот подход) предотвратит любое потенциальное состояние гонки, поскольку первый, кто получит блокировку, выполнит свою работу со всеми другими запросами, ожидающими снятия блокировки (или тайм-аутом) , в зависимости от того, как вы хотите, чтобы ваше приложение работало.)
Мясо статьи здесь:
SP_getapplock
- оболочка для расширенной процедуры XP_USERLOCK
. Это позволяет использовать механизм блокировки SQL SERVER для управления параллелизмом вне области таблиц и строк. Вы можете использовать его для маршалинга вызовов PROC таким же образом, как и вышеупомянутые решения, с некоторыми дополнительными функциями.
Sp_getapplock
добавляет блокировки непосредственно в память сервера, что снижает ваши накладные расходы.
Во-вторых, вы можете указать время ожидания блокировки без необходимости изменять настройки сеанса. В случаях, когда вы хотите, чтобы выполнялся только один вызов для определенного ключа, быстрый тайм-аут обеспечил бы, чтобы процедура не задерживала выполнение приложения очень долго.
В-третьих, sp_getapplock
возвращает статус, который может быть полезен при определении того, должен ли код вообще выполняться. Опять же, в случаях, когда вам нужен только один вызов для определенного ключа, код возврата 1 скажет вам, что блокировка была успешно предоставлена после ожидания снятия других несовместимых блокировок, таким образом, вы можете выйти без выполнения дополнительного кода (например, проверка существования, например).
Синакс выглядит следующим образом:
sp_getapplock [ @Resource = ] 'resource_name',
[ @LockMode = ] 'lock_mode'
[ , [ @LockOwner = ] 'lock_owner' ]
[ , [ @LockTimeout = ] 'value' ]
Пример использования sp_getapplock
/************** Proc Code **************/
CREATE PROC dbo.GetAppLockTest
AS
BEGIN TRAN
EXEC sp_getapplock @Resource = @key, @Lockmode = 'Exclusive'
/*Code goes here*/
EXEC sp_releaseapplock @Resource = @key
COMMIT
Я знаю, что это само собой разумеется, но поскольку объем блокировок sp_getapplock
является явной транзакцией, обязательно SET XACT_ABORT ON
или включите проверки в коде, чтобы гарантировать, что ROLLBACK происходит там, где это необходимо.