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

Ayo*_*o I 2 sql-server concurrency stored-procedures 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
Run Code Online (Sandbox Code Playgroud)

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

当前事务尝试更新自此事务启动以来已更新的记录.交易中止.在批处理结束时检测到不可提交的事务.该事务被回滚.

我是否正确使用sp_getapplock?似乎我建议的方法应该有效.

小智 6

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

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

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

希望这有帮助.