2016-09-29 50 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',我认为这对于容易获取查询来说非常重要(我们的查询很快,直到所有连接都被使用,然后发生错误,应用程序完全死机,我们可以重新启动它。 ) –