【问题标题】:WCF Service Causes SQL Deadlock ErrorWCF 服务导致 SQL 死锁错误
【发布时间】:2013-02-14 21:44:45
【问题描述】:

在过去的几天里,我一直在努力解决这个问题,但我似乎无法想出一个好的解决方案。

我有一个 WCF 服务,它充当我们所有数据库交互的唯一入口点。在当前的困境中,有一个 Windows 服务针对 Microsoft Dynamics CRM 中的“AsyncOperation”表旋转。每当在 CRM 中创建实体记录时,Windows 服务都会从 AsyncOperation 表中提取记录,并使用该数据向 WCF 服务发出请求。我遇到的问题是,当 Windows 服务同时向 WCF 服务发出多个请求时,该服务会导致 SQL 中的事务死锁。

我在 WCF 服务内的数据流中添加了一些额外的日志记录,我发现在任何给定时间,3 到 5 个请求可能会在几毫秒内到达服务。似乎在这个过程中,第一个命中服务的请求是唯一进入数据库的请求,其余的最终导致 sql 死锁错误:

事务(进程 ID 95)在锁定资源上死锁 另一个进程并已被选为死锁受害者。重新运行 交易。

我的问题是:有没有一种好的方法来实现某个时间的排队过程或 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;
}

【问题讨论】:

  • 你的 WCF 服务负载平衡了吗?
  • 最初它是通过 WSO2 esb 传递代理的性质。但是我们在 ESB 上遇到了一些网络流量问题,因此我们推迟了实施。到目前为止,它只是对 WCF 服务的严格调用。

标签: sql wcf asynchronous dynamics-crm-2011 deadlock


【解决方案1】:

您应该查看您使用的存储过程,以免产生死锁。 Sql 旨在与多个请求并行工作。有一些方法可以审查代码并进行改进,以免陷入僵局。

如果这不可能,您可以使用服务代理将您的请求排队到 sql server(如果您使用支持它的 sql server 版本)。这意味着您必须稍后检查您的操作结果。

如果仍然不是这种情况(ms-sql

如果您可以使用CLR存储过程,您可以使用操作结果调用您的WCF服务,这样您就不需要定期检查DB。

希望这会有所帮助。

作为编辑,您还可以检查 SP 中的死锁并在出现错误时重试(检查错误号 1205)

【讨论】:

  • 谢谢。这很有帮助。 CLR 方法真的很酷,但我认为这对于我们正在尝试做的事情来说可能有点矫枉过正。我来看看存储过程,看看能不能再优化一点。
  • 我解决了死锁问题,方法是将存储过程中的操作包装在一个 try/catch 中,如果错误是死锁,该 try/catch 具有重试逻辑。我是一个 sql 新手,所以这被我偷走了dwdmbi.com/2012/01/deadlock-recovery-using-try-catch-in.html
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多