EF核心和大流量導致最大池大小達到錯誤

.net-core asp.net asp.net-web-api c# entity-framework-core

我們使用ASP.NET Entity Framework Core在我們的Web API應用程序中查詢我們的MSSQL數據庫。有時,當我們有大量流量時,查詢到DB會以此錯誤結束:

超時已過期。從池中獲取連接之前經過的超時時間。這可能是因為所有池連接都在使用中並且達到了最大池大小。

我想知道我們使用DbContext和查詢的模式是否正確或者我是否缺少某些使用/ dispose模式和錯誤是由於某些內存洩漏引起的(在我讀過一些研究之後我不應該使用因為生命週期由框架管理)。我正在關注文件 ......

我的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依賴注入時,我認為這是一個糟糕的模式。

一般承認的答案

我認為問題是由將數據庫上下文查詢中的對象存儲到內存緩存中引起的 。我有一個大的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()寫入所有子查詢時,它會FirstOrDefault()幫助,因為分佈式緩存需要物化對象。

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


熱門答案

您可以在startup.cs中設置DbContext的生命週期,看看是否有幫助:

    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中不可能(但是?)。

可以使用顯式事務在此處找到解決方法:

    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:根據線程,將從.NET Core 1.2版開始支持Transactions。

@mukundabrt這是由dotnet / corefx#2949跟踪的。請注意,TransactionScope已經移植到.NET Core,但只能在.NET Core 1.2中使用。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因