列出慢性能与每一个慢性能

本文关键字:性能 慢性 每一个 | 更新日期: 2023-09-27 17:49:41

我正在构建程序,使用数据库与3个表(Worker, Task, TaskStep)我有一个方法,获取日期和建立报告为特定的工人

数据库结构如下:

MySQL 5.2

Worker表列:

workerID(VARCHAR(45)),
name(VARCHAR(45)),
age(int),
...

Tasks表列:

TaskID(VARCHAR(45)),
description(VARCHAR(45)),
date(DATE),
...

TaskSteps表列:

TaskStepID(VARCHAR(45)),
description(VARCHAR(45)),
date(DATE),
...

没有索引

问题是它非常非常慢!(~ 20秒)

代码如下:

using WorkerDailyReport = Dictionary<task, IEnumerable<taskStep>>;
private void Buildreport(DateTime date)
{
    var report = new WorkerDailyReport();    
    // Load from DB
    var sw = new Stopwatch();
    sw.Start();
    var startOfDay  = date.Date;
    var endOfDay    = startOfDay.AddDays(1);
    var db          = new WorkEntities();
    const string    workerID   = "80900855";
    IEnumerable<task> _tasks = db.task
                    .Where(ta =>    ta.date     >= startOfDay   &&
                                    ta.date     <  endOfDay     &&
                                    ta.workerID == workerID)
                    .ToList();
    sw.Stop();
    Console.WriteLine("Load From DB time - " + sw.Elapsed + 
                      ", Count - "           + _tasks.Count());   
    // Build the report
    sw.Restart();
    foreach (var t in _tasks)
    {
        var ts = db.taskStep.Where(s => s.taskID == task.taskID);
        report.Add(t, ts);
    }
    sw.Stop();
    Console.WriteLine("Build report time - " + sw.Elapsed);
    // Do somthing with the report
    foreach (var t in report)
    {
        sw.Restart();
        foreach (var subNode in t.Value)
        {
            // Do somthing..
        }
        Console.WriteLine("Do somthing time - " + sw.Elapsed + 
                          ", Count - " + t.Value.Count());
    }
}

正如你所看到的,我把秒表在每个部分检查什么需要这么长时间结果如下:

1)

如果我运行以上代码:

控制台:

Load From DB time - 00:00:00.0013774, Count - 577
Build report time - 00:00:03.6305722
Do somthing time - 00:00:07.7573754, Count - 21
Do somthing time - 00:00:08.2811928, Count - 11
Do somthing time - 00:00:07.8715531, Count - 14
Do somthing time - 00:00:08.0430597, Count - 0
Do somthing time - 00:00:07.7867790, Count - 9
Do somthing time - 00:00:07.3485209, Count - 39
.........

内部foreach每次运行大约需要7-9!!我不想再碾过去了40记录。

2)

如果我只改变了一件事,在第一个查询之后添加.ToList()当我从数据库加载工作任务时,它会发生变化everithing .

控制台:

Load From DB time - 00:00:04.3568445, Count - 577
Build report time - 00:00:00.0018535
Do somthing time - 00:00:00.0191099, Count - 21
Do somthing time - 00:00:00.0144895, Count - 11
Do somthing time - 00:00:00.0150208, Count - 14
Do somthing time - 00:00:00.0179021, Count - 0
Do somthing time - 00:00:00.0151372, Count - 9
Do somthing time - 00:00:00.0155703, Count - 39
.........

现在从DataBase加载需要更多的时间,4秒以上。但是build报告时间大约是~1ms每个内foreach需要~10ms

第一种方式是不可能的(577 * ~8秒),第二种选择它也很慢,我看不见。

你知道这里发生了什么吗?

1)为什么ToList()这么慢?

2)为什么没有ToList(),内部foreach和构建报告正在变慢?

我怎样才能使它更快?

非常感谢。

列出慢性能与每一个慢性能

当你不使用。tolist()时,c#不会从数据库中加载数据,直到你第一次需要从数据库中获取数据,这是因为实体框架中的延迟加载。

,在for-each循环的每一步中,你的程序都向数据库请求查询,这是非常慢的。

但是,当您使用. tolist()时,您立即运行查询并首先获得所有记录,这很慢。然后,在内部for-each循环中,你的程序拥有内存中的所有记录。

D

LINQ ToList()总是立即计算序列-在您的情况下数据库上的SQL查询。

在第一个实例中,你得到Load From DB time - 00:00:00.0013774, Count - 577 -这是快速的,因为你没有运行SQL查询。然而,查询是稍后运行的-这就是为什么您得到Build report time - 00:00:03.6305722 (slow)。

在第二个实例中,添加ToList()立即强制计算查询(执行SQL),这就是为什么您得到以下时间:

  • Load From DB time - 00:00:04.3568445, Count - 577 - SQL查询数据库(slow)
  • Build report time - 00:00:00.0018535 -对内存中已经存在的数据进行操作(fast)
有趣的是,您的查询返回577项花费超过3秒。这可能是因为其中一个表上缺少索引。

请记住,如果表上没有索引,数据库系统需要执行全表扫描以找出满足以下条件的所有项:

.Where(ta => ta.date >= startOfDay &&
             ta.date < endOfDay &&
             ta.workerID == workerID)

随着Tasks表中的条目数量的增长,您的查询将花费越来越长的时间

所以我强烈建议在Tasks.dateTasks.workerId列上创建索引。这将改善初始查询时间(假设您的数据库连接很快,即您没有连接到部署在海洋上的数据库)。

顺便说一句,不要在所有表列上创建索引(只在查询条件中使用的列上创建索引)。这可能会减慢插入操作的速度,并增加数据库的大小。

不幸的是,我不能建议更多关于Do somthing time ...,因为你没有提供代码。但如果你采用同样的建议,我相信你也会得到一些改进。

为了提高性能,应该使用一个查询从所有表中获取数据:

var _joinedTasks = db.task.Where(ta =>    ta.date     >= startOfDay   &&
                                    ta.date     <  endOfDay     &&
                                    ta.workerID == workerID)
                    .Join(db.taskStep, t => t.taskID, ts=>ts.taskID, (t, ts) => new {t, ts})
                    .GroupBy(g => g.t, v=>v.ts).AsEnumerable();

然后你可以添加到dictionary:

var report = _joinedTasks.ToDictionary(g=>g.Key);