所以我开发了一个查询数据库的仪表板。该数据库的数据存储在谷歌分析中,用于我们拥有的网站。
我使用ASP.NET MVC 5,EF,Linq和Telerik控件/小部件。控制器实例化一个服务层,我有我的数据库上下文和业务逻辑。每个svc.method()都属于一个特定的结果集,之后我在VM中打包以解压缩到视图中的一个小部件。
目前,Google Chrome网络标签中的响应时间为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;
}
}
答案 0 :(得分:2)
以下是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查询中是临时的,在内存查询中是一个final。这是因为需要ToShortDateString()
方法不支持SQL查询。在大多数情况下,SQL查询中的单个最终投影就足够了。)
答案 1 :(得分:0)
一些提示:
索引 - 出现在select操作的where子句中的索引列,使用SQL分析器检测“表扫描”操作并添加索引以避免它们(用索引搜索或聚簇索引搜索替换它们)
缓存 - 将上面的SQL profiler中的跟踪存储到DB中的表(SQL Profiler可以执行此操作)并通过sql文本对SQL命令进行分组,这可能会显示一些可以通过缓存避免的重复选择
Glimpse - 可以计算每个Web请求的SQL命令,如果Web应用程序尚未优化,则该数字可能会令人惊讶。 Glimpse可以说明更多信息,例如Web服务器在Web服务器上花费了多长时间,以及Web浏览器渲染页面的时间。
作为最后的手段,为最常见的查询编写自己的SQL