2

我有大约 20 个存储过程,它们相互消耗,形成树状依赖链。

然而,存储过程使用内存中的表进行缓存,并且可以从许多不同的客户端同时调用。

为了防止对内存表的并发更新/删除尝试,我使用 sp_getapplock 和SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT ON;.

我正在使用每个存储过程唯一的存储过程参数的散列,但是对具有相同参数的相同存储过程的多个并发调用应该生成相同的散列。正是这种对具有相同参数的相同存储过程的并发调用的哈希相等性为我提供了一个有用的资源名称来获取我们的应用锁。

下面是一个例子:

BEGIN TRANSACTION

EXEC @LOCK_STATUS = sp_getapplock @Resource= [SOME_HASH_OF_PARAMETERS_TO_THE_SP], @LockMode = 'Exclusive';

...some stored proc code...

IF FAILURE
BEGIN
  ROLLBACK;
  THROW [SOME_ERROR_NUMBER]
END

...some stored proc code...

COMMIT TRANSACTION

尽管将所有内容包装在应该阻止任何并发更新或删除的 applock 中,但我仍然收到错误 41302:

当前事务试图更新自该事务开始以来已更新的记录。交易被中止。在批处理结束时检测到不可提交的事务。事务被回滚。

我是否错误地使用了 sp_getapplock?看来我建议的方法应该有效。

4

1 回答 1

6

在您使用内存优化表开始事务的那一刻,您将获得基于事务开始时间的“快照”,以实现乐观的并发解决方案。不幸的是,您的锁在拍摄快照后就位,因此仍然完全有可能出现乐观的并发解析失败。

考虑一下需要相同锁的 2 个事务同时开始的情况。他们都在获得锁或修改任何行之前“同时”开始他们的事务。他们的快照看起来完全一样,因为尚未修改任何数据。接下来,一个事务获得锁并继续进行更改,而另一个事务被阻塞。这个事务提交很好,因为它是第一个,所以它引用的快照仍然与内存中的数据匹配。另一个事务现在获得了锁,但它的快照无效(但它还不知道这一点)。它继续执行,最后意识到它的快照是无效的,所以它会抛出一个错误。说实话,事务甚至不需要同时开始,第二个事务只需要在第一次提交之前开始。

您需要在应用程序级别强制执行锁定,或者通过在会话级别使用 sp_getapplock。

希望这有帮助。

于 2014-01-21T16:03:16.467 回答