TransactionScope细节问题

3

假设我有两个线程,它们在具有ReadCommitted隔离级别的线程特定TransactionScopes中执行一些面向数据库的代码。但是有一张表的数据应该是共享的(不应创建重复项)。

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted))
{
   ...//some code
   if (!_someRepository.IsValueExists(value))
      _someRepository.AddData(value);
   ...//some code
   transactionScope.Complete();
}

问题在于两个线程可能几乎同时检查数据是否存在,如果不存在,则创建重复的数据(约束不起作用:我必须防止异常情况发生)。我想这是一个琐碎的问题,但通常如何解决呢?
我认为以下是一种可行的解决方案:
using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted))
{
   ...//some code
   transactionScope.IsolationLevel = IsolationLevel.ReadUncommitted; //change Isolation Level
   lock (_sharedDataLockObject)
   {
      if (!_someRepository.IsValueExists(value))
         _someRepository.AddData(value);
   }
   transactionScope.IsolationLevel = IsolationLevel.ReadCommitted; //reset IsolationLevel
   ...//some code
   transactionScope.Complete();
}

这个解决方案的第一个问题是TransactionScope不支持隔离级别修改。但是,让我们想象一下,在这里使用ADO.NET事务。不过我不确定它是否有效。

Snapshot 隔离级别能否帮助您的情况?该级别通过复制修改行来减少锁定。这样,其他事务仍然可以读取旧数据,而无需等待解锁。 - whyleee
@whyleee:但我的问题是关于添加记录而不是修改的。 - SiberianGuy
但是如果您需要锁定,则“ReadCommitted”隔离级别会锁定其他事务的带写锁记录。或者不是这样吗? - whyleee
ReadCommitted 的问题在于其他事务只有在提交后才能看到修改。 - SiberianGuy
你是否有必要使用 ReadCommited?否则,你不可以使用 Serializable 吗? - Simon Mourier
2个回答

2
在这种情况下,我会进行双重检查。
首先检查它是否不存在,这里不需要事务。
然后开始一个串行化事务。
检查它仍然不存在
如果不存在,则添加
提交并关闭事务。

你应该尝试重新组织你的代码,使事务变得非常短小。你的代码似乎在一个事务中太过冗长。 - Shiraz Bhaiji
但是如果我需要能够回滚所有操作,我有其他选择吗? - SiberianGuy
你可以编写补偿代码来撤销更改,但这可能需要大量的工作。你需要查看你的应用程序设计以及在单个事务中更改了多少数据。 - Shiraz Bhaiji
我喜欢你关于双重检查和可串行化事务的想法。但是它太过严格了。我曾经实现过这样的方案,但锁定太多了。我希望有一些替代方案(从我的角度来看,我的问题似乎相当琐碎)。 - SiberianGuy

1

我通常使用数据库约束来解决相同的问题,并在try-catch中包装整个事务,以便在catch块中进行重试。当然,如果由于某些原因无法重新启动事务,则这不是有效的解决方案(例如,事务是在您控制之外启动的-我不确定您所说的“我必须防止异常情况发生”是什么意思)。

根据事务通常需要多长时间,您可能需要在重试之前等待一段时间或进行几次重试,但只要并行事务已成功完成,您的重试就会成功。

棘手的部分通常是如何确定异常是由特定的约束违规引起的。这通常需要进行一些经验测试,以确定确切的异常类型和异常消息的一些丑陋的字符串匹配。


我找到了与你所描述的类似的解决方案。唯一的细微差别是,我使用并行编程原语来控制并行事务。在并行执行的情况下,有些地方肯定会导致事务死锁,因此我不允许超过一个线程进入这些块。 - SiberianGuy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接