2010-05-20 67 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,序列化似乎是要走的路。