簡體   English   中英

Entity Framework Core - 同時運行的查詢在不同的環境中表現不同

[英]Entity Framework Core - Query ran simultaneously behaves differently on different environment

問題:我注意到同時啟動的實體框架調用的處理時間越來越長。 程序是.NET Core 2.2,帶EF 2.2

以下是演示該行為的日志示例:

來自日志的快照

並且在第 30 次查詢時最多持續 10 秒。

調查:

為了重現和測試這一點,我創建了一個簡單的可執行文件,它將並行調用方法 Test() 20 次,Test() 從 ServiceProvider 獲取作用域上下文,並對數據庫進行查詢。 我已經執行了幾次,並顯示了每個設置的 3 個結果的屏幕截圖。 注意:Query() 方法在 20 次測試之前運行一次,允許 EF 生成查詢緩存

 private async Task Test()
    {
        List<Task> tasks = new List<Task>();
        // Populate the queue.
        for (int i = 0; i < 10000; i++)
        {
            cq.Enqueue(i);
        }
        Query();
        Thread.Sleep(1000);
        Query();
        Thread.Sleep(1000);
        for (int i = 0; i < 20; i++)
        {
            tasks.Add(Task.Run(() => Query()));
        }


        await Task.WhenAll(tasks);

    }



    private async Task Query()
    {
        cq.TryDequeue(out int id);

        Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: started");

        using (var serviceScope = _serviceProvider.CreateScope())
        {
            var watch = new Stopwatch();
            var ctx = serviceScope.ServiceProvider.GetService<Core.Models.MyContext>();

            watch.Start();

            var quoteLineJob = await ctx.QuoteLineJobs
                .FirstOrDefaultAsync(o => o.Id == id);

            watch.Stop();

            Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");
            
        }
    }

我無法理解結果,希望有人能提出一個假設:

  • 從生產客戶端機器到生產數據庫產品到產品

加載時間越來越長

  • 從開發機器到生產數據庫

DevToProd

但是,訪問相同的數據庫時,結果要慢得多。

  • 隨機服務器到生產數據庫

ServToProd

結果與開發人員的機器非常相似,但是,它有 50% 的機會隨機運行(參見第三個結果)而不是增量

我想補充一點,查看 SQL Server Profiler,對於所有設置,所有同時查詢的讀取查詢需要 0 毫秒。

好像這並不令人困惑,當我在 localhost 上嘗試相同的測試時:

  • 開發人員的機器到開發人員的數據庫(生產中的數據庫復制,相同的數據)

開發到開發

這里的結果是隨機的,不遞增

  • 隨機服務器到隨機服務器數據庫

服務到服務

同樣,加載時間是隨機的,不會增加

有沒有人看到一些模式和建議,為什么 Entity Framework 會這樣?

我的第一個建議是確定原因是 EF 還是您的服務包裝器,具體取決於實現方式。 改為將此測試更改為並檢查不同環境的性能:

var watch = new Stopwatch();
watch.Start();
using (var ctx = new YourAppDbContext(ConnectionString))
{
    var quoteLineJob = await ctx.QuoteLineJobs
        .FirstOrDefaultAsync(o => o.Id == id);
}
watch.Stop();

Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");
        

如果這表現得更可預測,那么您的上下文 scope 或 DbContext 的提供者就會出現問題。 (不太可能,但沒有看到代碼,有可能)

如果它仍然有些可變,這可能是並行化的症狀。 在執行異步代碼時,等待的方法將設置恢復執行點並釋放正在執行的線程。 .Net 將響應恢復線程操作的請求。 鑒於這些操作已經被並行化到任務中,一項測試是同步進行 EF 調用以評估是否恢復了更可預測的性能。 服務器的核心數和整體負載情況可能會極大地影響 20 個或更多並行化的執行方式。

var watch = new Stopwatch();
watch.Start();
using (var ctx = new YourAppDbContext(ConnectionString))
{
    var quoteLineJob = ctx.QuoteLineJobs
        .FirstOrDefault(o => o.Id == id);
}
watch.Stop();

Log.Debug($"[{id}] {DateTime.Now.ToString("mm:ss.fff")}: Quote line job loaded in {watch.ElapsedMilliseconds}ms");

同樣,這只是為了幫助確定性能難題的潛在原因。 async / await不等於更好的性能,它們使單個操作變慢,但使整個服務器對處理更多請求的響應更快,而無需等待較慢或高頻任務完成后再接收請求。 線程之間的上下文切換將意味着每個任務的運行速度都會變慢,並且可能會在服務器處於負載狀態時發生變化。 我會將async操作保留給預計會花費大量時間或預計會非常頻繁地調用的任務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM