2011-12-07 2 views
0

Hibernate 질의가 데이터를 가져 오는 곳 (즉, sedison 캐시 또는 2 차 레벨 캐시 또는 db)을 결정하는 신뢰할 수있는 좋은 방법이 있습니까? 나는 다음과 같은 특성을 가진 최대 절전 모드를 구성하는 경우 : (그들은에서 검색되는 어디 모르겠지만)Hibernate Profiling

<prop key="hibernate.show_sql">true</prop> 
<prop key="hibernate.format_sql">true</prop> 

나는 실행 선택에 대한 정보의 톤을받을 수 있습니다.

private static final class HibernateStatistics implements Serializable { 
    private static final long serialVersionUID = 1L; 
    private long queryExecutions = 0; 
    private long transactions = 0; 
    private long entityLoads = 0; 
    private long connects = 0; 
    private long time = 0; 
    private double secondLevelHits = 0; 
    private double secondLevelMisses = 0; 
    private double queryHits = 0; 
    private double queryMisses = 0; 

    public HibernateStatistics(Statistics stats) { 
    synchronized(stats) { 
     queryExecutions = -stats.getQueryExecutionCount(); 
     transactions = -stats.getTransactionCount(); 
     entityLoads = -stats.getEntityLoadCount(); 
     connects = -stats.getConnectCount(); 
     secondLevelHits = -stats.getSecondLevelCacheHitCount(); 
     secondLevelMisses = -stats.getSecondLevelCacheMissCount(); 
     queryHits = -stats.getQueryCacheHitCount(); 
     queryMisses = -stats.getQueryCacheMissCount(); 
     time = -System.currentTimeMillis(); 
    } 
    } 

    public void update(Statistics stats) { 
    synchronized(stats) { 
     queryExecutions += stats.getQueryExecutionCount(); 
     transactions += stats.getTransactionCount(); 
     entityLoads += stats.getEntityLoadCount(); 
     connects += stats.getConnectCount(); 
     secondLevelHits += stats.getSecondLevelCacheHitCount(); 
     secondLevelMisses += stats.getSecondLevelCacheMissCount(); 
     queryHits += stats.getQueryCacheHitCount(); 
     queryMisses += stats.getQueryCacheMissCount(); 
     time += System.currentTimeMillis(); 
    } 
    } 

    @Override 
    public String toString() { 
    return "Stats" 
    + "[ queries=" + queryExecutions 
    + ", xactions=" + transactions 
    + ", loads=" + entityLoads 
    + ", connects=" + connects 
    + ", queryCacheHits=" + queryHits 
    + ", secondLevelCacheHits=" + secondLevelHits 
    + ", time=" + time + " ]"; 
    } 
} 
:

여기
public class ProfilingInterceptor implements HandlerInterceptor { 

    @Autowired private SessionFactory sessionFactory; 
    private static final String STATS = "hibernateStats"; 
    private static final String START_TIME = "startTime"; 
    private static final Logger LOGGER = Logger.getLogger(ProfilingInterceptor.class); 

    @Override public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler) throws Exception { 
    request.getSession().setAttribute(START_TIME, System.currentTimeMillis()); 
    request.getSession().setAttributes(STATS, new HibernateStatistics(sessionFactory.getStatistics()); 
    return true; 
    } 

    @Override public boolean postHandle(HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler) throws Exception { 
    HibernateStatistics stats = (HibernateStatistics) 
           request.getSession().getAttribute("STATS"); 
    stats.update(sessionFactory.getStatistics()); 
    request.getSession().setAttribute(STATS, new HibernateStatistics(sessionFactory.getStatistics())); 
    LOGGER.debug(stats); 
    } 

    @Override public boolean postHandle(HttpServletRequest request, 
            HttpServletResponse response, 
            Object handler, 
            Exception ex) throws Exception { 
    long startTime = (Long) request.getSession().getAttribute(START_TIME); 
    long currentTime = System.currentTimeMillis(); 
    request.getSession().setAttribute(START_TIME, null); 
    long totalTime = currentTime - startTime; 
    LOGGER.debug("URI: " request.getRequestURI() + " Method: " 
       + request.getMethod() + " took " + totalTime + "ms."); 
    HibernateStatistics stats = (HibernateStatistics) 
           request.getSession().getAttribute(STATS); 
    stats.update(sessionFactory.getStatistics()); 
    LOGGER.debug(stats); 
    } 
} 

(이것은 내부 클래스의)처럼 HibernateStatistics 객체가 모습입니다 : 이것은 스프링 MVC 응용 프로그램입니다, 우리의 모든 요청이 봄 컨트롤러로 처리되어 있기 때문에 나는 다음 인터셉터를 생성

저는 코드에서 N + 1 쿼리 문제가 발생할 수있는 상황을 찾기 위해 단일 스레드 테스트를 활용 해 왔지만이 시점에서 우리의 연결 수와 트랜잭션 수는 모든 페이지에서 잘 나타납니다.

이제는 두 번째 레벨 및 쿼리 캐싱이 얼마나 효과적인지 판단하는 좋은 방법이 필요합니다. 문제는 HibernateStatistics가 쓰레드에 안전하지 않다는 것이다. 그래서 우리가 멀티 쓰레드를 시도 할 때이 값들에 대해 이상한 숫자를 볼 수있었습니다. 통계를 수집하고 추가 분석을 위해 페이지에 표시하는 것만 큼 간단합니까?

답변

2

요청이있는 주변의 통계를 전달하는 이유는 무엇입니까? SessionFactory를 싱글 톤으로 구성했다고 가정하면 sessionFactory.getStatistics()는 응용 프로그램에서 호출 할 때마다 항상 동일한 Statistics 객체를 반환합니다. 통계 로깅을 위해 HibernateStatistics 클래스를 사용할 필요가 없습니다. 통계를 기록 할 수있는 정적 util 메소드 만 구현하면됩니다. 세션 팩토리는 통계가 초기화 된 순간부터 누적됩니다.

I 선택 사항이 로그에서 db에서로드 중임을 나타냅니다. 세션/2 차 레벨 캐시의 경우, 최대 절전 모드로 쿼리가 기록되지 않습니다. 캐시의 성능을 확인하는 유일한 신뢰할 수있는 방법은 캐시 통계의 히트/미스 비율을 모니터링하는 것입니다. 엔티티 수준에서 통계를 사용하도록 설정할 수도 있습니다. Check here.

+0

내가 넘겨주는 이유는 숫자가 바뀌는 것을 쉽게 볼 수 있기 때문입니다. 요청의 각 단계마다 숫자를 인쇄 할 수는 있지만 수동으로 계산해야합니다. 세션에서 (미리 무효화 된) 세션을 유지하면 자바, 이후 UI에서 얼마나 많은 트랜잭션, 연결 등이 수행되었는지 빠르게 볼 수 있습니다. 로그에있는 선택에 대해 놓친 문서가 있습니까? 일부 쿼리는 캐시되지만 실제로는 인쇄됩니다. – Scott