2014-02-19 36 views
1

我们目前遇到了用ASP.NET MVC 4编写的网站的性能问题,特别是Linq-to-SQL的开销,从而导致(其中包括)CPU跳转到对于使用Linq-to-SQL的每个页面请求,100%。Linq-to-SQL开销

我正在使用ANTS Performance Profiler 8.5 Pro分析应用程序,并在开发和生产环境中体验到相同的结果。然而,生产环境中的性能比开发环境中的性能差得多。

通过蚂蚁我似乎已经缩小到高架实例LINQ到SQL数据上下文和执行例如像一个非常简单的查询时:

using (var db = new D.DataClassesDataContext(ConfigurationManager.ConnectionStrings["Master"].ConnectionString)) 
{ 
    db.ObjectTrackingEnabled = false; 

    HttpContext.Cache["WebPageTemplateCapabilities"] = (from x in db.Clients 
                 where x.ClientID == this.ClientID 
                 select x.WebPageTemplateCapabilities).SingleOrDefault(); 
} 

我不能为我的生活弄清楚为什么一个简单的查询会导致这样的性能下降。这段代码片段最多需要1.5秒才能运行,与SSMS中执行的相同查询相比,这是疯狂的。我在这一点上假设高CPU使用率可能是由本网站进行的任何数据库通信的结果。

我错过了什么?这是为了记录在网站生命周期中实例化和查询所执行的第一个数据上下文。

+0

如何以及在何处调用此代码? – Andrei

+0

它构成了一个对象的构造函数,它首先通过基本控制器中另一个对象的实例化来实例化。 – Maritim

+0

您是否尝试明确管理连接? (这可以防止可能的连接池),例如db.Connection.Open()和db.Connection.Close() –

回答

1

问题是您正在使用SingleOrDefault。该方法必须保证只有一个匹配您的查询的结果。您应该使用FirstOrDefault而不是只读取第一个条目(如果有的话)。

我也会避免为相同的请求打开几个数据库连接(因为您似乎只为那个查询使用专用连接)。

而是为所有HTTP请求相关查询共享相同的连接。确实存在ADO.NET连接池,但它仍会给你带来开销。特别是如果有几个用户浏览您的网站。

您可以使用HTTP模块

public class ConnectionModule : IHttpModule 
{ 
    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += OnBeginRequest; 
     context.EndRequest += OnEndRequest; 
    } 

    private void OnEndRequest(object sender, EventArgs e) 
    { 
     var db = ((HttpApplication)sender).Context.Items["DbConnection"] as IDbConnection; 
     if (db != null) 
      db.Dispose(); 
    } 

    private void OnBeginRequest(object sender, EventArgs e) 
    { 
     var conString = ConfigurationManager.ConnectionStrings["Master"].ConnectionString; 
     var connection = new SqlConnection(conString); 
     connection.Open(); 
     ((HttpApplication)sender).Context.Items["DbConnection"] = connection; 
    } 

    public void Dispose() 
    { 
    } 
} 

现在,您可以通过访问从任何地方连接:

var connection = (IDbConnection)HttpContext.Current.Items["DbConnection"]; 

当然你也可以替换为您DbContext而不是连接。

至于工作单位。在我的世界中,HTTP请求始终代表一个工作单元。否则,HTTP请求太宽泛。 (但这当然取决于你如何设计你的网站)

+0

我肯定会尝试解决FirstOrDefault与SingleOrDefault有关的问题。建议您在整个网络中使用一个上下文工作单元,您如何建议实现这种连接共享?静态上下文? – Maritim

+0

@Maritim:已添加示例。 – jgauffin

+0

根据我的经验,即使在班级的基础上,共享的环境可能会出现可怕的错误,我主张对工作单元采取更细化的方法。 –

3

如果你正在使用Linq to SQL并且你多次调用同一个查询,那么我建议使用编译函数。上面的查询可以写成如下:

private static Func<DataClassesDataContext, string, string> MyCompiledFunction = 
     CompiledQuery.Compile((DataClassesDataContext pContext, string pClientID) => 
      (from x in pContext.Clients 
      where x.ClientID == pClientID 
      select x.WebPageTemplateCapabilities).SingleOrDefault(); 

您不指定返回类型或客户端ID,所以我假定它们是字符串。

请注意,首次编译函数时会产生开销 - 之后它会大幅度提高速度,所以我们的代码中速度提高了5到6倍。如果它只被称为一次,那么不要打扰。

+0

它只调用一次,然后存储在HttpContext.Cache中,如代码示例所示。 – Maritim

+0

那么不要麻烦了。如果你有其他方法匹配,那么肯定会重写它们。我发现LINQ to SQL有时极其缓慢,这对我们帮助非常大。 – Andrew

+0

我有许多方法可以与此匹配,但是我担心编译查询可能会造成更多的伤害而不是更好。您采取了哪些其他措施来提升业绩? – Maritim