【问题标题】:NHibernate concurrents threaded insertions with unique constraintNHibernate 并发线程插入具有唯一约束
【发布时间】:2012-06-06 07:29:13
【问题描述】:

我有一个多线程应用程序,它可能会同时插入一些具有标记为唯一属性的相同类型对象。

public class Foo(){
...
    string PropertyThatshouldBeUnique {get;set;}
...
}

每个线程都有自己的会话并且确实:

Foo myFooInstance = new Foo();
myFooInstance.PropertyThatshouldBeUnique = "Bar";
myThreadSession.SaveOrUpadte(myFooInstance);
  • 我的数据库表上有一个唯一约束,它阻止了 多次插入,因此第二次出现异常 插入触发整个事务的回滚(不好)
  • 并发插入可能非常接近(几毫秒)
  • 我没有配置任何具体的Nhibernate Concurrency strategy(不确定这是否能解决我的问题或使用哪一个)

我的问题是:

我应该如何以及在代码中的何处检查以前插入的具有相同属性值的 Foo 对象?

【问题讨论】:

  • "...触发整个事务的回滚(不好)" 为什么你觉得这样不好呢?你预计会发生什么?
  • 嗨,迭戈。这不好,因为我不想处理回滚(执行了许多其他操作)。理想情况下,我想要一个检查对象是否已经存在的过程,这样我就不会尝试将它插入两次
  • 那么,当要插入重复密钥时,您想要防止做任何事情,对吧?做到这一点的唯一方法是使用排他锁,这对可伸缩性很不利。请问为什么会有并发线程经常尝试使用重复键插入?
  • 实际上,如果对象存在,我想要的是执行“更新”而不是“插入”(这意味着我在执行第二次 SaveOrUpdate 调用之前设法得到它)。唯一键可以被视为主键,但我没有这样定义它,因为它是一个长字符串,而且我的全局模式使用整数作为 pkey。

标签: c# multithreading nhibernate concurrency


【解决方案1】:

你能在 NHibernate 中做这样的事情而不破坏你当前的架构吗?

if(!Update(connection))
{
    using (var command = new SqlCommand(@"INSERT INTO foo VALUES (Bar, ...)", connection))
    {
        try
        {   
            command.ExecuteNonQuery(); 
        }               
        catch (BlahUniqueConstraintException) // dummy exception, please replace with relevant
        {
            // very rarely will get in here
            Update(connection);
        }           
    }
}           

private bool Update(SqlConnection connection)
{
    // use update to do two things at once: find out if the record exists and also ... update
    using (var command = new SqlCommand(@"UPDATE foo SET ... WHERE PropertyThatshouldBeUnique = 'Bar'", connection))
    {
        // if the record exists and is updated then returns 1, otherwise 0
        return command.ExecuteNonQuery() > 0;
    }   
}

【讨论】:

  • 好吧,您确实很好地解释了什么可以解决我的问题。但是:1)我想坚持使用 Nhibernate 语法(即:“session.SaveOrUpdate(myObject)”)和 2)插入查询是具有许多其他查询的事务的一部分,因此它仅在我执行提交时执行我的工作单元结束,所以基本上如果发生异常,我想我必须处理需要在事务中执行的所有其他查询
【解决方案2】:

这是很久以前发布的,但是当我发现自己处于同样的情况时,让我告诉你我找到了哪些解决方案。

对于 DB 操作,它们不是让每个线程通过 Nhibernate 完成工作,而是将请求发送到另一个正在执行某种服务工作的线程。它公开了一些方法,例如 Insert。

线程1要插入Object1

线程 2 想要插入 Object1

他们正在同时执行类似 Thread3.Insert(Object1) 的操作。

第一次调用Thread3接收,它打开一个到数据库的会话(无成本),它用SELECT检查对象1不在数据库中,然后添加它。

第二次调用Thread3 接收,即使它晚了一纳秒,它也会执行相同的操作,但在SELECT 之后停止并且什么也不做或发回异常。

等待处理第一个调用的成本非常低,毕竟您通过需要检查的数据库约束(因此数据库访问)限制了多线程速度

这就像说,嗯,我有 2 台速度非常快的 PC 和 1 台低服务器。

我认为这更像是一个设计问题,而不是 Nhibernate 功能。

线程之间的同步已经很复杂了,但是使用UnitOfWork模式的线程之间的DB同步很痛苦。

(这是您的问题之一,因为您每次插入时都不会提交交易)

也许 SO 的一些大师有更好的想法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    相关资源
    最近更新 更多