是否可以查看hibernate的queryPlanCache内容?

jecbmhm3  于 2023-03-23  发布在  其他
关注(0)|答案(1)|浏览(164)

我在Hibernate 5.2.10.Final中遇到了queryPlaneCache的内存泄漏问题。我发现了IN()查询的泄漏问题,并将其标记为setCacheable(false)。泄漏减少了,但没有消失。
我尝试了decrasehibernate.query.plan_cache_max_size和关闭缓存(hibernate.cache.use_query_cache)。这很有帮助,但我认为这不是正确的解决方案。我想将一些有问题的查询标记为setCacheable(false),并保存缓存机制在良好的工作中,但没有关于麻烦查询的一些事情。
由于这个原因,我想转储一个queryPlaneCache内容,以查看具体的SQL查询并找到问题的来源。这可能吗?

shstlldc

shstlldc1#

当然可以,如果你可以通过一点反射来访问私有字段的话。我就是这样做的。getQueryPlanCacheDump()返回一个表示缓存查询的字符串列表,缓存中存储的其他信息将被丢弃(它可能不需要诊断你的问题)
当我这样做的时候,我发现我的缓存已经满了,因为IN子句在每次使用不同数量的参数进行查询时都会缓存一个新的查询计划。

private final EntityManagerFactory entityManagerFactory;

  public List<String> getQueryPlanCacheDump() {
    try (var session = entityManagerFactory.createEntityManager().unwrap(Session.class)) {
      var sessionFactory = (SessionFactoryImpl) session.getSessionFactory();
      return getQueryList(sessionFactory);
    }
  }

  private List<String> getQueryList(SessionFactoryImpl sessionFactory) {
    BoundedConcurrentHashMap<?, ?> queryPlanCache = getQueryPlanCache(sessionFactory);
    var queries = new ArrayList<String>();
    try {
      var hqlQueryPlanKeyClazz = Class.forName("org.hibernate.engine.query.spi.QueryPlanCache$HQLQueryPlanKey");
      var filterQueryPlanKeyClazz = Class.forName("org.hibernate.engine.query.spi.QueryPlanCache$FilterQueryPlanKey");
      queryPlanCache.forEach((key, value) -> {
        // The cache contains 3 different types of keys
        if (key instanceof NativeSQLQuerySpecification nativeSql) {
          queries.add(nativeSql.getQueryString());
        } else if (hqlQueryPlanKeyClazz.isInstance(key)) {
          queries.add(getQuery(hqlQueryPlanKeyClazz, key));
        } else if (filterQueryPlanKeyClazz.isInstance(key)) {
          queries.add(getQuery(filterQueryPlanKeyClazz, key));
        }
      });
    } catch (Exception e) {
      // If Hibernate adds more key types in the future, return all the key types instead so you can investigate adding support for them
      var keyClasses = new HashSet<String>();
      queryPlanCache.forEach((key, value) -> keyClasses.add(key.getClass().toString()));
      return keyClasses.stream().toList();
    }
    return queries;
  }

  private String getQuery(Class<?> clazz, Object key) {
    try {
      var queryField = clazz.getDeclaredField("query");
      queryField.setAccessible(true);
      return (String) queryField.get(key);
    } catch (IllegalAccessException | NoSuchFieldException e) {
      throw new IllegalStateException(e);
    }
  }

  private BoundedConcurrentHashMap<?, ?> getQueryPlanCache(SessionFactoryImpl sessionFactory) {
    try {
      var queryPlanCacheField = QueryPlanCache.class.getDeclaredField("queryPlanCache");
      queryPlanCacheField.setAccessible(true);
      return (BoundedConcurrentHashMap<?, ?>) queryPlanCacheField.get(sessionFactory.getQueryPlanCache());
    } catch (IllegalAccessException | NoSuchFieldException e) {
      throw new IllegalStateException(e);
    }
  }

相关问题