性能优化实体框架查询

本文关键字:查询 框架 实体 优化 性能 | 更新日期: 2023-09-27 18:04:32

所以我开发了一个查询数据库的仪表板。 该数据库中存储了来自我们拥有的网站的谷歌分析的数据。

我正在使用 ASP.NET MVC 5,EF,Linq和Telerik控件/小部件。 控制器实例化一个服务层,我在其中拥有数据库上下文和业务逻辑。 每个 svc.method(( 都与一个特定的结果集有关,我将其打包到 VM 中以解包到视图中的小部件中。

目前,谷歌浏览器网络选项卡中的响应时间为5.6秒。 我已经说明了 8 种方法中的一种来向您展示我的方法。

我的问题是;如何提高性能以使页面加载速度更快? 使每个方法异步会改善它吗?

提前感谢您提供的任何建议。

控制器:

    public ActionResult WebStats()
    {
        //other code removed for brevity
        //Service layer where the db is queried and the business logic is performend
        WebStatsService svc = new WebStatsService();
        //view model 
    WebStatsViewModel vm = new WebStatsViewModel();
        vm.PageViews = svc.GetPageViews(vm);
        vm.UniquePageViews = svc.GetUniquePageViews(vm);
        vm.UserRatioByCountry = svc.GetUserRatioByCountry(vm);
        vm.PageViewsByCountry = svc.GetPageViewsByCountry(vm);
        vm.TopTenHealthCenters = svc.GetTopTenHealthCenters(vm);
        vm.UserTypeRatio = svc.GetUserTypeRatio(vm);
        vm.TopTenHealthCentersByDateRange = svc.GetTopTenHealthCentersByDateRange(vm);
        vm.ReferralSources = svc.GetTopTenReferralSources(vm);//Get top 10 referral paths

        return View(vm);
    }

服务:

    public List<PageViews> GetPageViews(WebStatsViewModel vm)
    {
        using (ApplicationDbContext db = new ApplicationDbContext())
        {
            List<PageViews> pageViewStats = new List<PageViews>();
            var results = db.PageStats.Where(x => (vm.CMS.Equals("All") || x.Source.Equals(vm.CMS))
                                               && (vm.HealthCenter.Equals("All") || x.HealthSectionName.Equals(vm.HealthCenter))
                                               && (vm.Country.Equals("All") || x.Country.Equals(vm.Country))
                                               && (vm.City.Equals("All") || x.City.Equals(vm.City))
                                               && (x.Date >= vm.StartDate)
                                               && (x.Date <= vm.EndDate)
                                            ).Select(x => new
                                            {
                                                Date = x.Date,
                                                Total = x.PageViews
                                            }).ToList();
            var distinctDate = results.OrderBy(x => x.Date).Select(x => x.Date).Distinct();
            foreach (var date in distinctDate)
            {
                PageViews pageViewStat = new PageViews();
                pageViewStat.Date = date.Value.ToShortDateString();
                pageViewStat.Total = results.Where(x => x.Date == date).Sum(x => x.Total);
                pageViewStats.Add(pageViewStat);
            }
            return pageViewStats;
        }
    }

性能优化实体框架查询

下面是有关

EF 查询的一些提示:

(1( 避免在动态过滤器中混合常量和实际谓词,如下所示:

(vm.CMS.Equals("All") || x.Source.Equals(vm.CMS))

它可能看起来很简洁,但生成的SQL很糟糕且效率低下。相反,请使用if语句和链接Where

// Base query including static filters
var query = db.PageStats.AsQueryable();
// Apply dynamic filters
if (!vm.CMS.Equals("All"))
    query = query.Where(x => x.Source.Equals(vm.CMS));
// ...
// The rest of the query
query = query.Select(...

(2( 尝试从 SQL 查询中返回尽可能少的数据。

例如,您的查询正在用(Date, Total)对填充列表,然后您手动(并且不是非常有效地(按Date分组并获取Sum(Total)。相反,可以使 EF 查询直接返回该分组/聚合数据。

将所有这些应用于您的示例将导致如下所示:

using (ApplicationDbContext db = new ApplicationDbContext())
{
    var query = db.PageStats
        .Where(x => x.Date >= vm.StartDate && x.Date <= vm.EndDate);
    if (!vm.CMS.Equals("All"))
        query = query.Where(x => x.Source.Equals(vm.CMS));
    if (!vm.HealthCenter.Equals("All"))
        query = query.Where(x => x.HealthSectionName.Equals(vm.HealthCenter));
    if (!vm.Country.Equals("All"))
        query = query.Where(x => x.Country.Equals(vm.Country));
    if (!vm.City.Equals("All"))
        query = query.Where(x => x.City.Equals(vm.City));
    query = query
        .GroupBy(x => x.Date)
        .Select(g => new
        {
            Date = g.Key,
            Total = g.Sum(x => x.PageViews)
        })
        .OrderBy(x => x.Date);
    var pageViewStats = query
        .AsEnumerable() // SQL query ends here
        .Select(x => new PageViews
        {
            Date = x.Date.Value.ToShortDateString(),     
            Total = x.Total
        })
        .ToList();
    return pageViewStats;
}

您可以尝试将性能与原始性能进行比较。

(注意:对于此特定查询,我们需要使用两个投影 - 一个在 SQL 查询中临时,另一个在内存查询中最后一个。这是因为需要 SQL 查询不支持ToShortDateString()方法。在大多数情况下,SQL 查询中的单个最终投影就足够了。

一些提示:

  1. 索引 - 出现在选择操作的 where 子句中的索引列,使用 SQL 探查器检测"表扫描"操作并添加索引以避免它们(用索引搜索或聚集索引搜索替换它们(

  2. 缓存 - 将上面的 SQL 探查器的跟踪存储到数据库中的表中(SQL 探查器可以做到这一点(并按 sql 文本对 SQL 命令进行分组,这可能会显示一些可以通过缓存避免的重复选择

  3. 一瞥 - 可以计算每个 Web 请求的 SQL 命令,如果 Web 应用程序尚未优化,则数量可能会令人惊讶。Glimpse可以告诉更多,例如,Web请求的总时间中有多少时间在服务器上花费,以及Web浏览器中呈现页面的时间是多少。

  4. 作为最后的手段,为最公开的查询编写自己的 SQL