【问题标题】:How to reprocess a deadlocked action from http request如何重新处理来自 http 请求的死锁操作
【发布时间】:2014-05-19 19:43:14
【问题描述】:

问题是,当 2 个进程相互锁定时,SQL Server 有时会选择一个会话作为其死锁牺牲品。一个进程进行更新,而另一个只是读取。在读取期间,SQL Server 会创建所谓的“共享锁”,它不会阻止其他读取器,但会阻止更新器。到目前为止,解决这个问题的唯一方法是重新处理受害线程。

现在这发生在 Web 应用程序中,我希望有一种机制可以在需要时进行重新处理(比如说最多 5 次)。

我查看了IHttpModule,其中调用了BeginRequest()EndRequest() 事件(以及其他事件),但这并不能让我重新处理请求。

事实上,我需要的是在 http 处理程序和被调用的进程之间强制执行的东西。

我可以这样写:

int maxtries = 5;

while(maxtries > 0)
{
    try
    {
        using(var scope = Session.OpenTransaction())
        {
            // process

            scope.Complete(); // commit

            return result;
        }
    }
    catch(DeadlockException dlex)
    {
        maxtries--;
    }
    catch(Exception ex)
    {
        throw;
    }
}

但我必须为所有乏味且容易出错的请求编写该代码。如果我可以通过自动调用的 Web.Config 配置一种重新处理处理程序并为我执行处理死锁重新处理,那我会很好。

【问题讨论】:

  • 您使用什么网络技术? WebForms、MVC、Web API?
  • 我知道您想在 C# 中执行此操作,但您可能需要考虑在 SQL 中重新尝试。看看我们在研究死锁问题时发现的这篇文章。 codeproject.com/Articles/42547/…
  • 我们使用微软的 MVC4
  • 使用 SQL 不是我们的选择。我们使用 ORM (DataObjects.Net) 进行数据访问
  • DataObjects.Net 有一个再处理库,但它或多或少地完成了我在我的小示例代码中编写的内容,但更高级。

标签: c# sql-server http deadlock


【解决方案1】:

如果您遇到死锁,则说明您的数据库层有问题。您缺少索引或类似的东西,或者您在锁定依赖实体的事务中进行了无序更新。

无论使用 HTTP 作为处理此错误的机制,都不是可行的方法。 如果您确实需要重试死锁,那么您应该将尝试包装在您自己的函数中,然后几乎完全按照上面的描述重试。

但是我强烈建议您找出死锁的原因并解决它。

希望这听起来不要太轻视您的问题,但要解决问题的原因而不是症状。

【讨论】:

    【解决方案2】:

    由于您使用 MVC 并假设在 DB 故障时重新运行整个操作是安全的,您可以简单地编写一个通用的控制器基类,所有控制器都将从该类继承(如果您还没有控制器) ,并在其中覆盖 OnActionExecuting 并捕获特定异常并重试。这样,您将只在一个地方拥有代码,但再次假设在这种情况下重新运行整个操作是安全的。

    例子:

    public abstract class MyBaseController : Controller
    {
        protected override void OnActionExecuting(
        ActionExecutingContext filterContext
            )
        {
            int maxtries = 5;
    
            while(maxtries > 0)
            {
                try
                {
                    return base.OnActionExecuting(filtercontext);
                }
                catch(DeadlockException dlex)
                {
                    maxtries--;
                }
                catch(Exception ex)
                {
                    throw;
                }
            }
    
            throw new Exception("Persistent DB locking - max retries reached.");
        }
    }
    

    ...然后简单地更新每个相关控制器以从该控制器继承(同样,如果您还没有通用控制器)。

    编辑:B/w,Bigtoe 的回答是正确的 - 死锁是原因,应该相应地处理。如果无法可靠地修复 DB 层,上述解决方案确实是一种解决方法。第一次尝试应该是审查和(重新)构造查询,以便首先避免死锁。只有在不可行的情况下才应采用上述解决方法。

    【讨论】:

      猜你喜欢
      • 2018-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多