【问题标题】:Which isolation level should I use for the following insert-if-not-present transaction?我应该为以下 insert-if-not-present 事务使用哪个隔离级别?
【发布时间】:2010-05-20 21:12:18
【问题描述】:

我编写了一个实质上执行 ETL 任务的 linq-to-sql 程序,并且我注意到并行化可以提高其性能的许多地方。但是,当两个线程执行以下任务(伪代码)时,我担心防止违反唯一性约束。

Record CreateRecord(string recordText)
{
    using (MyDataContext database = GetDatabase())
    {
        Record existingRecord = database.MyTable.FirstOrDefault(record.KeyPredicate());
        if(existingRecord == null)
        {
            existingRecord = CreateRecord(recordText);
            database.MyTable.InsertOnSubmit(existingRecord);
        }

        database.SubmitChanges();
        return existingRecord;
    }
}

通常,此代码执行SELECT 语句来测试记录是否存在,如果记录不存在,则执行INSERT 语句。它被隐式事务封装。

当两个线程为recordText 的同一个实例运行此代码时,我想防止它们同时确定记录不存在,从而尝试创建相同的记录。隔离级别和显式事务会很好地工作,除了我不确定我应该使用哪个隔离级别——Serializable 应该工作,但似乎太严格了。有更好的选择吗?

【问题讨论】:

    标签: .net sql linq-to-sql transactions isolation-level


    【解决方案1】:

    我使用类似于如下所示的 SQL 来避免这种情况。 UPDLOCK 指定在事务完成之前获取并保持更新锁,HOLDLOCK 等效于 SERIALIZABLESERIALIZABLE 通过将共享锁保持到事务完成来使共享锁更具限制性,而不是在不再需要所需的表或数据页时立即释放共享锁,无论事务是否已完成。执行扫描的语义与在 SERIALIZABLE 隔离级别上运行的事务相同。 HOLDLOCK 仅适用于为其指定的表或视图,并且仅适用于由使用它的语句定义的事务的持续时间。HOLDLOCK 不能在包含 @987654330 的 SELECT 语句中使用@ 选项。

    declare @LocationID          int
    declare @LocationName        nvarchar (50)
    
    /* fill in LocationID and LocationName appropriately */
    
    INSERT dbo.Location
    (LocationID, LocationName)
    SELECT @LocationID, @LocationName
    WHERE NOT EXISTS (
       SELECT L.*
       FROM dbo.Location L WITH (UPDLOCK, HOLDLOCK)
       WHERE L.LocationID = @LocationID)
    

    根据this question的回答,Serializable似乎是要走的路。

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-02
      • 1970-01-01
      • 2011-09-30
      • 2015-07-21
      相关资源
      最近更新 更多