sp_getapplock用于同步对内存表的并发访问

时间:2013-12-18 09:40:59

标签: sql-server stored-procedures concurrency sql-server-2014

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

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

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

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

以下是一个例子:

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

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

  

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

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

1 个答案:

答案 0 :(得分:6)

第二个使用内存优化表开始事务时,您将获得“快照”(基于事务启动的时间),以获得乐观的并发解析。不幸的是,在拍摄快照后你的锁就位了,所以仍然完全有可能让乐观的并发解决方案失败。

考虑两个需要同一锁的事务一次开始的情况。它们在获得锁定或修改任何行之前“同时”开始它们的事务。他们的快照看起来完全一样,因为还没有修改过任何数据。接下来,一个事务获得锁定并继续进行更改而另一个事务被阻止。此事务提交正常,因为它是第一个,因此它引用的快照仍然匹配内存中的数据。另一个事务现在获得锁定,但它的快照无效(但它还不知道这个)。它继续执行,最后意识到它的快照无效,因此它会引发错误。实际上,事务甚至不需要同时启动,第二个事务只需要在第一次提交之前启动。

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

希望这会有所帮助。