简体   繁体   English

Entity Framework Core - 同时运行的查询在不同的环境中表现不同

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

Problem : I have noticed that Entity Framework calls started at the same time are getting incrementally longer to process.问题:我注意到同时启动的实体框架调用的处理时间越来越长。 The program is .NET Core 2.2, with EF 2.2程序是.NET Core 2.2,带EF 2.2

Here is an example of logs that demonstrate the behavior:以下是演示该行为的日志示例:

来自日志的快照

and it goes on to up to 10 seconds at the 30th query.并且在第 30 次查询时最多持续 10 秒。

Investigation:调查:

To reproduce and test this, I have created a simple executable that will call a method Test() 20x times in parallel, Test() is getting the scoped context from ServiceProvider, and doing a query to the database.为了重现和测试这一点,我创建了一个简单的可执行文件,它将并行调用方法 Test() 20 次,Test() 从 ServiceProvider 获取作用域上下文,并对数据库进行查询。 I have executed it few times and showing a screenshot from 3 results on each setup.我已经执行了几次,并显示了每个设置的 3 个结果的屏幕截图。 Note: the Query() method is run once before the 20 tests, allowing EF to generate the query cache注意: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");
            
        }
    }

I can't make sense of the results and hope someone could suggest a hypothesis:我无法理解结果,希望有人能提出一个假设:

  • From Production Client machine to Production DB从生产客户端机器到生产数据库产品到产品

the loading is incrementally longer加载时间越来越长

  • From developer machine to Production DB从开发机器到生产数据库

DevToProd

Hitting the same database, however, results are much slower.但是,访问相同的数据库时,结果要慢得多。

  • Random Server to Production DB随机服务器到生产数据库

ServToProd

The result is pretty similar as from the developer's machine, however, 50% chance that it behaves randomly (See 3rd result) and not incrementally结果与开发人员的机器非常相似,但是,它有 50% 的机会随机运行(参见第三个结果)而不是增量

I would like to add that looking at a SQL Server Profiler, for all setup, the read queries take 0ms for all simultaneous queries.我想补充一点,查看 SQL Server Profiler,对于所有设置,所有同时查询的读取查询需要 0 毫秒。

As if this wasn't confusing, when I tried the same test on localhost:好像这并不令人困惑,当我在 localhost 上尝试相同的测试时:

  • Developer's machine to developer's DB (DB Copy from production, same data)开发人员的机器到开发人员的数据库(生产中的数据库复制,相同的数据)

开发到开发

results here are random and not incremented这里的结果是随机的,不递增

  • Random Server to Random Server DB随机服务器到随机服务器数据库

服务到服务

Again, loading times are random and not incremented同样,加载时间是随机的,不会增加

Does anyone see some patterns, and suggestions as to why Entity Framework would behave like this?有没有人看到一些模式和建议,为什么 Entity Framework 会这样?

My first suggestion would be to determine whether the cause is EF or your service wrapper, depending on how that is implemented.我的第一个建议是确定原因是 EF 还是您的服务包装器,具体取决于实现方式。 Change your test to this instead and check the performance on the different environments:改为将此测试更改为并检查不同环境的性能:

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");
        

If this behaves more predictably then something is suss with your context scope or provider for the DbContext.如果这表现得更可预测,那么您的上下文 scope 或 DbContext 的提供者就会出现问题。 (unlikely, but without seeing the code, a possibility) (不太可能,但没有看到代码,有可能)

If it is still somewhat variable this is probably a symptom of parallelization.如果它仍然有些可变,这可能是并行化的症状。 While executing async code, the awaited methods will be setting resume execution points and freeing up the executing thread.在执行异步代码时,等待的方法将设置恢复执行点并释放正在执行的线程。 .Net will be responding to requests to resume thread operations. .Net 将响应恢复线程操作的请求。 Given these operations are already being parallelized into tasks one test would be to make the EF calls synchronously to assess whether more predictable performance comes back.鉴于这些操作已经被并行化到任务中,一项测试是同步进行 EF 调用以评估是否恢复了更可预测的性能。 The server's # of cores and overall load situation could greatly effect how 20 or more parallelized are executed.服务器的核心数和整体负载情况可能会极大地影响 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");

Again, this would just be to help identify the potential cause of performance quandaries.同样,这只是为了帮助确定性能难题的潜在原因。 async / await do not equal better performance, they make individual operations slower but make the server as a whole more responsive to handle more requests without waiting on slower or high-frequency tasks to complete before picking up a request. async / await不等于更好的性能,它们使单个操作变慢,但使整个服务器对处理更多请求的响应更快,而无需等待较慢或高频任务完成后再接收请求。 The context switching between threads will mean each task will operate slower and potentially variably so when the server is under load.线程之间的上下文切换将意味着每个任务的运行速度都会变慢,并且可能会在服务器处于负载状态时发生变化。 I would reserve async operations to tasks that are expected to take a considerable amount of time or expected to be called extremely frequently.我会将async操作保留给预计会花费大量时间或预计会非常频繁地调用的任务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM