【问题标题】:Hibernate Profiling休眠分析
【发布时间】:2012-01-15 00:37:54
【问题描述】:

是否有一种可靠的方法来确定 Hibernate 查询从何处获取数据(即 sesison 缓存或二级缓存或数据库)?如果我使用以下属性配置 Hibernate:

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

我收到了大量有关运行选择的信息(尽管我不知道从哪里检索它们)。由于这是一个 Spring MVC 应用程序,并且我们所有的请求都由 Spring 控制器处理,因此我创建了以下拦截器:

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 对象如下所示(它是一个内部类):

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 + " ]";
  }
}

我一直在使用单线程测试来查找代码中可能存在 N+1 查询问题的一些情况,但此时我们的连接数和事务数在所有页面上看起来都不错。

我现在需要一个很好的方法来确定我们的二级缓存和查询缓存的有效性。问题在于 HibernateStatistics 不是线程安全的。所以当我们尝试多线程时,我可以看到这些值的一些奇怪的数字。是否像收集统计数据并将其显示在页面上以供进一步分析一样简单?

【问题讨论】:

    标签: hibernate caching spring-mvc


    【解决方案1】:

    为什么要在请求中传递统计信息?假设您已将 SessionFactory 配置为单例,那么 sessionFactory.getStatistics() 将始终返回相同的 Statistics 对象,无论您在应用程序中调用它的次数和次数。您不需要使用 HibernateStatistics 类来记录统计信息。您可以只实现一个可以记录统计信息的静态 util 方法。会话工厂只会从初始化的那一刻起累积统计信息。

    我的选择在日志中,这意味着它们是从数据库加载的。如果它来自会话/二级缓存,则休眠不会记录查询。检查缓存性能的唯一可靠方法是监控缓存统计信息中的命中/未命中率。您还可以在实体级别启用统计信息。检查here.

    【讨论】:

    • 我传递它的原因是我可以很容易地看到数字的变化。我当然可以在请求的每个阶段打印出数字,但是我需要手动进行数学计算。将它们保留在会话中(预先否定)使我可以快速查看在 Java 中以及随后在 UI 中执行了多少事务、连接等。是否有关于日志中选择的文档我错过了 - 我知道有些查询被缓存了,但它们仍然被打印出来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多