2016-08-29 2 views
2

그래서 데이터베이스를 쿼리하는 대시 보드를 개발했습니다. 데이터베이스에는 Google 웹 로그 분석에서 Google에 보유한 데이터가 저장되어 있습니다.성능 튜닝 Entity Framework 쿼리

Telerik 컨트롤/위젯과 함께 ASP.NET MVC 5, EF, Linq를 사용하고 있습니다. 컨트롤러는 내 db 컨텍스트와 비즈니스 로직이있는 서비스 계층을 인스턴스화합니다. 각 svc.method()는 뷰 내의 위젯으로 패키지를 해제하기 위해 VM에서 패키지화 한 특정 결과 세트와 관련됩니다.

현재 Google 크롬의 네트워크 탭에서 응답 시간은 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

이 사이트에 대한 질문은 너무 광범위합니다. 비동기로 만들면 몇 가지 이점을 얻을 수 있지만 마일리지는 다를 수 있습니다. 우리는 귀하가 여기에서 귀하의 질문 중 하나 *를 조정하도록 도와 드릴 수 있지만 일반 레이아웃/실적은 조정할 수 없습니다. – DavidG

+0

감사합니다. @DavidG. 내가 쿼리 응답 시간을 개선 찾고,보기 성능은 다른 이야기입니다. 즉, 나는 다른 접근법에 대한 조언을 찾고있다. 각 위젯을 부분보기로 만들면 응답 성능이 향상됩니까? 나는 다른 전략에 대한 제안을 찾고있다. – JReam

+0

조언은 의견 일 뿐이며, 왜 이것이 적합하지 않은지에 대한 또 다른 이유입니다. 슬프다면 AJAX 호출을 통해 뷰에 데이터를로드하려는 유혹을받을 것입니다. 그러면 각 파트가 검색 될 때 페이지가 표시됩니다. – DavidG

답변

3
여기

몇 가지 팁을 자신의 SQL을 작성 :

(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에 의해 (Date, Total) 쌍, 당신이 수동으로 (그리고 매우 효율적으로) 그룹과 목록을 채우고 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 질의의 최종 프로젝션 하나만 있으면 충분합니다.)

+0

고마워, 이반. 계속해서 어떻게하면 내 linq 능력을 향상시킬 필요가 있는지 알 수있다 ... .AsEnumerable()까지 쿼리가 실행되지 않습니까? SQL 실행에 대한 귀하의 제안이 연기되고 실행되는시기가 궁금합니다. – JReam

+0

사실 그것은 'ToList' 호출에 의해 실행됩니다. AsEnumerable은 컨텍스트를 IQueryable (따라서 쿼리 공급자의 제어하에 있음)에서 IEnumerable (즉, LINQ to Objects - Enumerable 클래스에 포함 된 확장 메서드를 기반으로)로 전환합니다. 'IQueryable'에 메소드를 연결하는 한, SQL로 변환됩니다. IEnumerable 메소드는 메모리에서 실행됩니다. –

0

몇 가지 팁 :

  1. 인덱스 - 인덱스 컬럼에 SQL 프로파일 러를 사용하여 선택 작업의 where 절에 표시 '테이블 스캔'작업을 감지하고이를 방지하기 위해 색인을 추가합니다 (색인 검색으로 교체하거나 덱스 검색)

  2. 캐싱 -이

    를 캐싱하여 피할 수있는 몇 가지 반복 선택이 표시 될 수 DB (SQL 프로파일 러가 그것을 할 수 있습니다) 및 SQL은 SQL 텍스트로 명령 그룹의 테이블 위의 SQL 프로파일 러에서 추적을 저장
  3. Glimpse - 웹 요청 당 SQL 명령을 계산할 수 있지만 웹 응용 프로그램이 아직 최적화되지 않은 경우이 수치는 놀라운 것입니다. Glimpse는 훨씬 더 많은 것을 말해 줄 수 있습니다. 예를 들어 웹 요청의 총 시간이 서버에 소비 된 시간과 웹 브라우저에서 페이지를 렌더링하는 시간입니다.

  4. 은 최후의 수단으로, EF 쿼리에 가장 노출 쿼리