2016-03-01 83 views
1

首先,我不是多線程和並行編程方面的專家。Parallel.ForEach與NHibernate一起使用導致SQL Server鎖

我正試圖優化遺留應用程序的性能(.Net 4,NHibernate 2.1)。

**到目前爲止,升級NHibernate不是優先事項,但正在籌備中。

隨着時間的推移,性能已成爲數據增長的噩夢。我看到的一個項目是Parallel.ForEach語句,它調用一個方法來獲取和更新一個複雜的實體(具有多個關係 - propeties &集合)。

的一段代碼具有以下形式(爲了清楚而簡化):

void SomeMethod(ICollection<TheClass> itemsToProcess) 
{ 
    Parallel.ForEach(itemsToProcess, item => ProcessItem(item); 
} 

TheClass ProcessItem(TheClass i) 
{ 
    var temp = NHibernateRepository.SomeFetchMethod(i); 
    var result = NHibernateRepository.Update(temp); 
    return result; 
} 

SQL Server的間歇報告數據庫鎖定錯誤,錯誤如下:

Transaction (Process ID 20) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction

我懷疑這是由於一些競爭狀態導致死鎖,即使ISessions是分開的。

ICollection<TheClass>最多可以有1000項和每個屬性和處理後的子集,產生許多SELECTUPDATE語句(使用「NHibernate的探查器」確認)

有沒有更好的方式來處理這是以平行的方式,還是我應該重構代碼到傳統的循環?

我知道,我可以用替換地實現我的代碼:

  1. 一個foreach循環在同一ISession背景
  2. 和一個無狀態會話
  3. 隨着Environment.BatchSize設置爲一個合理的值

  1. 使用SQL BulkCopy

我也看過不少關於SQL服務器死鎖好的信息和Parallel.ForEach是一件容易的陷阱:

  1. SQL Transaction was deadlocked
  2. Using SQL Bulk Copy as an alternative
  3. Potential Pitfalls in Data and Task Parallelism
  4. Multi threading C# application with SQL Server database calls

回答

1

這是一個非常複雜的話題。有一種策略是保證安全的,並且可能是將導致加速:

在發生死鎖時重試。

由於死鎖回滾事務,您可以安全地重試整個事務。如果死鎖率低,並行加速將會很高。

重試的好處在於您可以在中心位置進行簡單的代碼更改。

由於從發佈的代碼中不明顯:確保線程不共享會話或實體。它們都不是線程安全的。

+0

感謝您的輸入。確實很複雜,可能的解決方案是多方面的。將嘗試您的重試策略。另一個增加更多扳手的複雜性是由於'NHibernate'密鑰生成策略導致的一些實體發生的多重'SELECTS'和'UPDATES'。正如我提到 – user919426

+0

hilo密鑰生成應該發生在一個單獨的事務中,如果NHibernate正確地執行它。不應該有涉及這個的僵局。你可以使用SQL Profiler來檢查。 – usr