2014-02-19 86 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