2016-09-29 78 views
2

我們使用ASP.NET實體框架的核心在我們的Web API應用程序查詢我們的MSSQL數據庫。有時,當我們有很大的流量,查詢到DB與此錯誤結束:EF核心和大流量導致最大池大小達到誤差

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

我不知道如果我們使用DbContext和查詢的模式是正確的,或者如果我錯過了一些使用/ Dispose模式和錯誤被一些引起內存泄漏(在我讀過一些研究後,我不應該使用,因爲生命週期是由框架管理的)。我下面documentation ...

我的connectionString:

"myConnection": "Server=xxx;Database=xxx;user id=xxx;password=xxx;Max Pool Size=200;Timeout=200;" 

我Startup.cs

public void ConfigureServices(IServiceCollection services) 
    { 
    ..... 
    // scoped context    
    services.AddDbContext<MyDbContext>(
      options => options.UseSqlServer(this.Configuration.GetConnectionString("myConnection"))); 
    } 

然後在控制器我用的DbContext的依賴注入:

public class MyController : Controller 
    public MyController (MyDbContext context) 
    { 
     this.Context = context;    
    } 

    public ActionResult Get(int id) 
    { 
     // querying 
     return this.Context.tRealty.Where(x=>x.id == id).FirstOrDefault(); 
    } 

應該我使用類似於:

using (var context = this.Context) 
     { 
      return this.Context.tRealty.Where(x => x.id == id).FirstOrDefault(); 
     } 

但我認爲這是錯誤的思路時,我使用的DbContext依賴注入。

+0

似乎你已經回答了你自己的問題。 –

+0

所以我應該在查詢通過依賴注入添加的dbcontext時使用__using__ ... –

+1

DI容器應該負責處理上下文,所以編號 –

回答

3

我認爲問題是由存儲從數據庫上下文查詢對象在內存中緩存引起的。我有一個大的LINQ查詢與其他一些子查詢內的數據庫上下文。我在主查詢結束時調用了FirstOrDefault(),但不在子查詢內。控制器很好,它默認實現查詢。

return this.Context.tRealty.AsNoTracking().Where(
       x => x.Id == id && x.RealtyProcess == RealtyProcess.Visible).Select(
       s => new 
       { ..... 

// subquery 
videos = s.TVideo.Where(video => video.RealtyId == id && video.IsPublicOnYouTube). 
         Select(video => video.YouTubeId).ToList()), // missing ToList() 
..... 
}).FirstOrDefault(); 

就有了問題 - 當他們在那裏儲存到在內存中緩存子查詢分別持有連接到數據庫上下文。當我實施Redis分佈式緩存時,它首先失敗了一些奇怪的錯誤。當我將ToList()FirstOrDefault()寫入我的所有子查詢時,它會有所幫助,因爲分佈式緩存需要物化對象。

現在我已將所有查詢明確地物化,並且我沒有最大池大小達到了錯誤。所以,當從數據庫上下文查詢中存儲對象到時,必須小心在內存高速緩存中。需要實現所有查詢以避免在內存中的某處保持連接。

1

您可以設置的DbContext的壽命在startup.cs,看看這可以幫助:

services.AddDbContext<MyDbContext>(options => options 
             .UseSqlServer(connection), ServiceLifetime.Scoped); 

另外,如果您的查詢是一個簡單的讀您可以通過使用.AsNoTracking()刪除跟蹤。

提高你的吞吐量的另一種方法是防止鎖使用事務段IsolationLevel.ReadUncommitted進行簡單的讀取。 如果你不想髒讀,你也可以使用Snapshot隔離級別 - 稍微限制一些。

TransactionOptions transactionOptions = new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted}; 
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions)) 
{ 
    // insert magic here 
} 

編輯:作爲提到的問題的作者,上面的代碼在EF Core中還沒有(但?)可能。

一種解決方法,可以發現here使用一個明確的交易:

using (var connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 

     using (var transaction = connection.BeginTransaction()) 
     { 
      // transaction.Commit(); 
      // transaction.Rollback(); 
     } 
    } 

我沒有測試過這一點。

編輯2:

   using (var c1= new SqlConnection(connectionString)) 
       { 
        c1.Open(); 
        // set isolation level 
        Exec(c1, "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); 
        Exec(c1, "BEGIN TRANSACTION;"); 
        // do your magic here 
       } 

以EXEC:

 private static void Exec(SqlConnection c, string s) 
     { 
      using (var m = c.CreateCommand()) 
      { 
       m.CommandText = s; 
       m.ExecuteNonQuery(); 
      } 
     } 

編輯3:您可以在那裏執行命令來設置隔離級別另一種未經測試的代碼段根據that線程,交易將得到支持從.NET Core 1.2開始。

@mukundabrt this is tracked by dotnet/corefx#2949. Note that TransactionScope has already been ported to .NET Core but will only be available in .NET Core 1.2.

+0

DbContext的生存期默認是作用域的(通過定義_AddDbContext_方法)... 使用'.AsNoTracking()'是個好主意,因爲我的API只讀,謝謝。但是我認爲這個問題在某個地方連接不好,_AsNoTracking_應該讓查詢更快。 在EF Core中,我認爲我不能使用'TransactionScope',因爲它現在不支持[link](https://github.com/aspnet/EntityFramework/issues/5595) –

+0

您的超時發生在連接級別或在查詢級別?您也可以嘗試在特定情況下設置Database.CommandTimeout屬性。 – Darxtar

+0

當我使用一些LINQ查詢時發生錯誤。我在連接字符串中設置了'TimeOut = 200',我認爲這對於容易獲取查詢來說非常重要(我們的查詢很快,直到所有連接都被使用,然後發生錯誤,應用程序完全死機,我們可以重新啓動它。 ) –