2013-02-28 115 views
1

在過去的幾天裏,我一直在毆打我的頭靠在牆上,我似乎無法想出一個好的解決方案。WCF服務導致SQL死鎖錯誤

我有一個WCF服務,充當我們所有數據庫交互的唯一入口點。在目前的困境中,有一種Windows服務針對Microsoft Dynamics CRM中的「AsyncOperation」表進行調整。無論何時在CRM中創建實體記錄,Windows服務都會將記錄從AsyncOperation表中提取出來,並使用該數據將請求發送到WCF服務。我遇到的問題是,當該Windows服務同時向WCF服務發出多個請求時,該服務會導致SQL中的事務死鎖。

我向WCF服務中的數據流添加了一些額外的日誌記錄,並且我發現在任何給定時間3到5個請求中的任何一個都可能在幾毫秒內觸擊服務。看來,在這個過程中擊中該服務的第一個要求就是把它做成了數據庫的唯一一個,其餘最終導致一個SQL死鎖錯誤:

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

我的問題是:是否有一個良好實現排隊過程的一些時間或單一方法到WCF,這將確保SQL插入不會在同一時間發生?由於我在MS Dynamics平臺之上構建,所以我沒有辦法確保多個請求不能同時處理。輸入到系統中的數據是由外部合作伙伴提供的,其中一些數據相當沉重。

我接受任何建議,我可以尋找解決方案。

在處理插入WCF服務的方法是這樣的:

public List<NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity> Insert(NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity businessObject) 
{ 
    List<NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity> businessObjectList = null; 

    using (SqlConnection conn = MainConnection) 
    { 
     int id = new Random().Next(9999); 
     try 
     { 
      conn.Open(); 

      using (SqlCommand cmd = new SqlCommand("[crud].[usp_Person_InsertUpdate]", conn)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 

       cmd.Parameters.Add(new SqlParameter("@iui_PersonId", businessObject.PersonId)); 
       cmd.Parameters.Add(new SqlParameter("@ii_PrefixId", businessObject.PrefixId)); 
       cmd.Parameters.Add(new SqlParameter("@ivn_FirstName", businessObject.FirstName)); 
       cmd.Parameters.Add(new SqlParameter("@ivn_MiddleName", businessObject.MiddleName)); 
       cmd.Parameters.Add(new SqlParameter("@ivn_LastName", businessObject.LastName)); 
       cmd.Parameters.Add(new SqlParameter("@ic_Gender", businessObject.Gender)); 
       cmd.Parameters.Add(new SqlParameter("@ii_SuffixId", businessObject.SuffixId)); 
       cmd.Parameters.Add(new SqlParameter("@ii_PersonTypeId", businessObject.PersonTypeId)); 
       cmd.Parameters.Add(new SqlParameter("@id_BirthDate", businessObject.BirthDate)); 
       cmd.Parameters.Add(new SqlParameter("@iti_PreferredContactMethodId", businessObject.PreferredContactMethodId)); 
       cmd.Parameters.Add(new SqlParameter("@iv_ModifiedUsername", businessObject.ModifiedUsername)); 

       Logger.Log(string.Format("-- PersonEntity Insert ({1}) Execute -- {0}", DateTime.Now, id)); 
       using (SqlDataReader rdr = cmd.ExecuteReader()) 
       { 
        businessObject = null; 
        businessObject = new NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity(); 

        businessObjectList = PopulateObjectsFromReader(rdr); 
       } 
       Logger.Log(string.Format("-- PersonEntity Insert ({1}) Complete -- {0}", DateTime.Now, id)); 
      } 
     } 
     catch(Exception ex) 
     { 
      throw new SeverityException(500, string.Format("PersonEntity::Insert ({0})::Error occured.", id), ex); 
      //throw new SeverityException(500, "PersonEntity::Insert::Error occured.", ex); 
     } 
    } 

    return businessObjectList; 
} 
+0

是您的WCF服務的負載平衡呢? – Daryl 2013-02-28 16:04:23

+0

最初它本來就是通過代理通過WSO2 esb的本質。但是我們遇到了ESB上的一些網絡流量問題,所以我們推遲了這個實施。截至目前,只有海峽呼叫WCF服務。 – keannan5390 2013-02-28 16:12:49

回答

3

您應該查看你爲了不產生死鎖使用存儲過程。 Sql旨在與多個請求並行工作。有許多方法可以查看代碼並進行改進,以免陷入僵局。

如果這不可能,您可以使用服務代理將您的請求排隊到sql server(如果您使用支持它的sql server版本)。這意味着您必須稍後檢查您的操作結果。

如果仍然不是這種情況(ms-sql < 2005),那麼你可以實現類似的東西。將您的請求寫入表格並使用工作來處理它。然後再次檢查操作結果。

如果您可以使用CLR存儲過程,則可以使用操作結果調用您的WCF服務,這樣就不需要定期檢查數據庫。

希望這會有所幫助。

作爲一個編輯,你還可以檢查您的SP僵局,如果重試錯誤出現(檢查錯誤沒有1205)

+0

謝謝。這很有幫助。 CLR方法非常酷,但我認爲對於我們要做的事情可能有點矯枉過正。我將看看存儲過程,看看它們是否可以優化得更好一些。 – keannan5390 2013-02-28 16:15:13

+1

我解決了死鎖問題,方法是將操作存儲在try/catch中的存儲過程中,如果錯誤是死鎖,那麼它將執行重試邏輯。我是一個新手一個SQL,所以這個由我滑倒 http://www.dwdmbi.com/2012/01/deadlock-recovery-using-try-catch-in.html – keannan5390 2013-02-28 16:32:44

+0

很酷..我很高興這工作爲你而出。 – 2013-02-28 16:55:20