【问题标题】:ASP.NET MVC LINQ Performance issue with two databases两个数据库的 ASP.NET MVC LINQ 性能问题
【发布时间】:2017-05-09 09:50:47
【问题描述】:

我在一个 ASP.NET MVC 应用程序中工作并且有两个不同的连接(数据库)。这就像一个应用程序,用户可以在其中进行一些小型考试。在一个页面上,我必须向用户显示所有包含详细信息的考试/活动:时间、结果以及有关活动/考试的所有详细信息。在红色数据库中仅包含有关结果、时间、用户和活动 ID 的信息 - 因此您可以在另一个(蓝色)数据库中获取该跟踪的活动。每个活动最多可进行 5 次。

我尝试了几个不同的查询,但性能非常非常慢。你有什么想法我该如何解决这个查询并且用户不必在页面上等待 10 秒?

谢谢!

编辑:

跟踪表(db1,表 1)现在有 1732982 条记录,并且一直在增长,因为用户可以随时进行考试。目前有 1832 项活动来自 110 节课。

下面的详细数据库图片(这是我得到的,所以我必须使用它,但如果需要我可以更改 ids/pk/fk - 或更改我需要的任何内容,但它必须保留在两个不同的数据库中):

这是用于为一位用户获取结果的代码。 UserId 像函数中的参数一样发送并在此代码中使用。

        IList<TrackingCustomModel> result = new List<TrackingCustomModel>();
        using (DATA.examContent.Model ctc = new DATA.examContent.Model()) //first database (activities, lessons)
        {
            using (Model ctx = new Model()) //this is second database (tracking)
            {
                // Get all Activities
                IList<Activity> ac = ctc.Activity.Include("Lesson").ToList();
                char[] trimChars = { '0' };
                IEnumerable<TrackingCustomModel> q;
                    q = from a in ac
                        select new TrackingCustomModel
                        {
                            ActivityId = a.Id,
                            LessonCode = a.Lesson.Code.TrimStart(trimChars),
                            ActivityCode = a.Code.TrimStart(trimChars),
                            ActivityHead = a.Head.Trim()
                        };
                IList<TrackingCustomModel> add = q.ToList<TrackingCustomModel>();
                // Get all Trackings for userId
                IList<Tracking> tr = ctx.Tracking.Where(t => t.UserId == userId).ToList();

                // Join Activites with Tracking of User, and Calculate Scores
                var s = from a in add
                        join t in tr on a.ActivityId equals t.ActivityId
                        orderby t.Timestamp descending
                        select new TrackingCustomModel
                        {
                            ActivityId = a.ActivityId,
                            LessonCode = a.LessonCode,
                            ActivityCode = a.ActivityCode,
                            ActivityHead = a.ActivityHead,
                            Timestamp = DefaultHelper.AdjustDateToLocalTimezone(t.Timestamp),
                            ActivityHits = t.ActivityHits,
                            ActivityMistakes = t.ActivityMistakes,
                            ActivityScore = (t.ActivityHits + t.ActivityMistakes) > 0 ? (String.Format("{0:0%}", (Decimal.Divide(t.ActivityHits, (t.ActivityHits + t.ActivityMistakes))))).ToString() : null
                        };


                // Group the results by Activity and calculate the Activity average

                var group = from t in s
                            group t by t.ActivityId into g
                            select g;
                foreach (IGrouping<Guid, TrackingCustomModel> g in group)
                {
                    TrackingCustomModel thc = (from t in g
                                               where t.ActivityId == g.Key
                                               select t).FirstOrDefault();

                    IList<TrackingCustomModel> multipleTry = (from d in s
                                                              where d.ActivityId == g.Key
                                                              select d).ToList();
                    if (multipleTry.Count > 1)
                    {
                        thc.ActivityDone = multipleTry.Count.ToString();
                        Decimal scoreAsDecimal;
                        Decimal averageScore = 0;
                        foreach (TrackingCustomModel t in multipleTry)
                        {
                            if (t.ActivityHits != 0)
                            {
                                scoreAsDecimal = (Decimal.Divide(t.ActivityHits, (t.ActivityHits + t.ActivityMistakes)));
                                averageScore += scoreAsDecimal;
                            }
                        }
                        averageScore /= multipleTry.Count;
                        if (averageScore != 0)
                        {
                            thc.AverageScore = String.Format("{0:0%}", averageScore).ToString();
                        }
                        else
                        {
                            thc.AverageScore = "";
                        }
                    }
                    else
                    {
                        thc.ActivityDone = "";
                    }
                    result.Add(thc);
                }
            }
        }
        return result;

【问题讨论】:

  • 您需要显示您正在执行的代码和查询。还有表格上的任何相关索引。
  • 因为您使用的是 LINQ 和两个不同的数据库,您可能会陷入一个常见的陷阱,即将每个结果集的所有数据拉回内存,然后使用 LINQ to 对象连接它们。除非您发布代码 sn-p,否则无法确定。
  • 什么是查询慢?
  • 我添加了一些信息和代码!谢谢大家!
  • 您应该测量代码中的步骤,创建addtr 和实际连接。确定最耗时的步骤。如前所述,连接是在内存中完成的。

标签: c# asp.net-mvc linq


【解决方案1】:
  1. 从数据库 1 中获取所有相关活动的列表。
  2. 从数据库 2 中获取所有活动信息 + 课程信息
  3. 找出慢的,1 还是 2?

慢动作列表查询(步骤一)

确保你索引任何你的 where 子句。例如。 UserId,可能包括 DateTime?

慢速活动信息/课程信息查询(第二步)

确保 Activity.ActiviyID 已编入索引,因为这是您应该在第二个查询中搜索的内容。

课程信息应该通过Lesson.LessonID搜索,应该被索引

如果使用EF,做一个

var res = Activities        
    .Include(x => x.Lesson) 
    .Where(x => activityIds.Contains(x.AcitivityID))
    .ToList()

从活动中将课程带到一个电话中。

否则如果使用自定义 SQL,请添加连接,例如

从表 1 中选择 * 在 a.LessonID = b.LessonID 上加入 Table2 b

并确保您正确映射它。

其他注意事项

  • 如果不经常更改,则缓存课程信息
  • 您有多少活动/课程
  • 您目前如何查询您的对象?确保您没有延迟加载整个树,因为这会导致您针对每个唯一实体访问数据库!

回复:编辑 1

不确定您的数据库中有多少数据,但请注意代码中的以下查询:

 IList<Activity> ac = ctc.Activity.Include("Lesson").ToList();
  1. 这将获得所有表格中的活动
  2. .Include("Lesson") 将复制具有相同 ID 的课程,这可能会导致无法返回的大量数据

例如如果您有 1 节课,但有 50 项活动,那么您将为这 50 项活动中的每一项检索一个单独的课程实例(即使它们是相同的)。

【讨论】:

  • 非常感谢您的回答!我编辑了我的问题,您现在可以详细查看数据库和我的代码,但无论如何我都会尝试您的建议并告诉您进展如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 2022-01-03
相关资源
最近更新 更多