并发检查失败时回滚事务
Rollback Transaction when concurrency check fails
我有一个存储过程,它对数据库进行大量探测以确定是否应更新某些记录
每条记录(订单)都有一个名为[RowVersion]
的TIMESTAMP
我将候选记录 ID 和 RowVersions 存储在名为 @Ids
的临时 table 中
DECLARE @Ids TABLE (id int, [RowVersion] Binary(8))
我得到以下候选人的数量
DECLARE @FoundCount int
SELECT @FoundCount = COUNT(*) FROM @Ids
由于记录可能会从我 SELECT
到我最终尝试 UPDATE
时发生变化,我需要一种方法来检查并发性,如果检查失败 ROLLBACK TRANSACTION
我目前有什么
BEGIN TRANSACTION
-- create new combinable order group
INSERT INTO CombinableOrders DEFAULT VALUES
-- update orders found into new group
UPDATE Orders
SET Orders.CombinableOrder_Id = SCOPE_IDENTITY()
FROM Orders AS Orders
INNER JOIN @Ids AS Ids
ON Orders.Id = Ids.Id
AND Orders.[RowVersion] = Ids.[RowVersion]
-- if the rows updated dosnt match the rows found, then there must be a concurrecy issue, roll back
IF (@@ROWCOUNT != @FoundCount)
BEGIN
ROLLBACK TRANSACTION
set @Updated = -1
END
ELSE
COMMIT
根据上面的内容,我正在使用存储的 [RowVersion]
过滤 UPDATE
这将跳过任何已更改的记录(希望如此)
但是我不太确定我是否在 TIMESTAMP
方面正确地使用事务或乐观并发,或者是否有更好的方法来实现我想要的目标
当您 SELECT 数据时,尝试在显式事务中使用 HOLDLOCK 和 UPDLOCK。它会扰乱其他事务的并发性,但不会扰乱你的事务。
很难理解您要实现的逻辑。
但是,如果您绝对必须在一个过程中执行多个非原子操作,并确保整个代码块在 运行 期间不会再次执行(例如,由另一个用户执行),考虑使用 sp_getapplock
.
Places a lock on an application resource.
您的程序可能与此类似:
CREATE PROCEDURE [dbo].[YourProcedure]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @VarLockResult int;
EXEC @VarLockResult = sp_getapplock
@Resource = 'UniqueStringFor_app_lock',
@LockMode = 'Exclusive',
@LockOwner = 'Transaction',
@LockTimeout = 60000,
@DbPrincipal = 'public';
IF @VarLockResult >= 0
BEGIN
-- Acquired the lock
-- perform your complex processing
-- populate table with IDs
-- update other tables using IDs
-- ...
END;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END
我有一个存储过程,它对数据库进行大量探测以确定是否应更新某些记录
每条记录(订单)都有一个名为[RowVersion]
TIMESTAMP
我将候选记录 ID 和 RowVersions 存储在名为 @Ids
DECLARE @Ids TABLE (id int, [RowVersion] Binary(8))
我得到以下候选人的数量
DECLARE @FoundCount int
SELECT @FoundCount = COUNT(*) FROM @Ids
由于记录可能会从我 SELECT
到我最终尝试 UPDATE
时发生变化,我需要一种方法来检查并发性,如果检查失败 ROLLBACK TRANSACTION
我目前有什么
BEGIN TRANSACTION
-- create new combinable order group
INSERT INTO CombinableOrders DEFAULT VALUES
-- update orders found into new group
UPDATE Orders
SET Orders.CombinableOrder_Id = SCOPE_IDENTITY()
FROM Orders AS Orders
INNER JOIN @Ids AS Ids
ON Orders.Id = Ids.Id
AND Orders.[RowVersion] = Ids.[RowVersion]
-- if the rows updated dosnt match the rows found, then there must be a concurrecy issue, roll back
IF (@@ROWCOUNT != @FoundCount)
BEGIN
ROLLBACK TRANSACTION
set @Updated = -1
END
ELSE
COMMIT
根据上面的内容,我正在使用存储的 [RowVersion]
过滤 UPDATE
这将跳过任何已更改的记录(希望如此)
但是我不太确定我是否在 TIMESTAMP
方面正确地使用事务或乐观并发,或者是否有更好的方法来实现我想要的目标
当您 SELECT 数据时,尝试在显式事务中使用 HOLDLOCK 和 UPDLOCK。它会扰乱其他事务的并发性,但不会扰乱你的事务。
很难理解您要实现的逻辑。
但是,如果您绝对必须在一个过程中执行多个非原子操作,并确保整个代码块在 运行 期间不会再次执行(例如,由另一个用户执行),考虑使用 sp_getapplock
.
Places a lock on an application resource.
您的程序可能与此类似:
CREATE PROCEDURE [dbo].[YourProcedure]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @VarLockResult int;
EXEC @VarLockResult = sp_getapplock
@Resource = 'UniqueStringFor_app_lock',
@LockMode = 'Exclusive',
@LockOwner = 'Transaction',
@LockTimeout = 60000,
@DbPrincipal = 'public';
IF @VarLockResult >= 0
BEGIN
-- Acquired the lock
-- perform your complex processing
-- populate table with IDs
-- update other tables using IDs
-- ...
END;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH;
END