2010-05-20 65 views
1

我已经写了一个基本上执行ETL任务的linq-to-sql程序,并且我注意到许多并行化将提高其性能的地方。但是,我担心在两个线程执行以下任务(伪代码)时防止出现唯一性约束违规。对于以下insert-if-present事务,我应该使用哪种隔离级别?

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应该可以工作,但似乎太严格了。有更好的选择吗?

回答

1

我使用类似于下面显示的SQL来避免这种情况。 UPDLOCK指定更新锁定将被执行直到交易完成并且HOLDLOCK等于SERIALIZABLESERIALIZABLE通过保持它们直到事务完成而使共享锁更具限制性,而不是在不再需要所需的表或数据页时释放共享锁,无论事务是否已完成。使用与在SERIALIZABLE隔离级别上运行的事务相同的语义执行扫描。 HOLDLOCK仅适用于指定的表或视图,并且仅适用于使用该语句的语句定义的事务处理期间。HOLDLOCK不能用于包含FOR BROWSE选项的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,序列化似乎是要走的路。