为什么 Hibernate 二级缓存在每次调用时执行 put 操作并花费大量时间?

问题描述 投票:0回答:1

我有一个 spring boot 2.7.3 应用程序,它使用 hibernate、hazelcast 作为二级缓存工厂、mysql。我的目标是使用二级缓存来查找所有查询并使查询更快。 数据库中有大约 5012 个实体用于开发目的。

我面临的问题是,当我第一次运行 findAll 方法时,它会从数据库中获取每个实体并将它们放入二级缓存。然而,在第二次查询后,它只命中了其中的 30 个,并再次将丢失的数据放入缓存。因此,这个放置过程需要大量时间并且系统速度变慢。当我关闭二级缓存时,很有趣,但系统运行得更快。

我的application.yml文件:

    properties:
      hibernate.jdbc.time_zone: UTC
      hibernate.id.new_generator_mappings: true
      hibernate.connection.provider_disables_autocommit: true
      hibernate.cache.use_second_level_cache: true
      hibernate.cache.use_query_cache: false
      hibernate.generate_statistics: false
      # modify batch size as necessary
      hibernate.jdbc.batch_size: 25
      hibernate.order_inserts: true
      hibernate.order_updates: true
      hibernate.query.fail_on_pagination_over_collection_fetch: true
      hibernate.query.in_clause_parameter_padding: true
      hibernate.cache.region.factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory

我向实体类添加了

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
的注释以使其可缓存。

我的 Hazelcast 配置:

    @Bean
    public Config hazelcastConfig() {
 
        Config config = new Config();
        config.setInstanceName("placeholder");

        MapConfig mapConfig = new MapConfig("default");
        mapConfig.setBackupCount(1);
        mapConfig.getEvictionConfig().setEvictionPolicy(EvictionPolicy.LRU);
        mapConfig.getEvictionConfig().setMaxSizePolicy(MaxSizePolicy.USED_HEAP_SIZE);
        mapConfig.setTimeToLiveSeconds(3600);
        config.addMapConfig(mapConfig);

        return Hazelcast.newHazelcastInstance(config);
    }

我有一个非常基本的查找所有方法:

    public List<SinkSourceAddDTO> findAll() {
        return HouseRepository.findAll()
       .stream().map(HouseAddMapper::toDto)
       .collect(Collectors.toCollection(LinkedList::new));
    }

会话指标: 第一个方法调用:

Session Metrics {
    1968100 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    41430100 nanoseconds spent preparing 32 JDBC statements;
    52658900 nanoseconds spent executing 32 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    2712459300 nanoseconds spent performing 5012 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    8894900 nanoseconds spent performing 30 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    97100 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections)
} 

将 5012 个条目放入缓存。

第二、第三等方法调用:

Session Metrics {
    290100 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    111100 nanoseconds spent preparing 2 JDBC statements;
    14781000 nanoseconds spent executing 2 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    1450759900 nanoseconds spent performing 4982 L2C puts;
    9978700 nanoseconds spent performing 30 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    11100 nanoseconds spent executing 2 partial-flushes (flushing a total of 0 entities and 0 collections)
} 

仅命中其中 30 个并将剩余内容放入缓存,这需要 1.4 秒才能完成。

我尝试了很多方法,例如迁移 EhCache、增加缓存大小、增加生存时间、机会驱逐策略等,但没有一个有效并解决问题。

spring hibernate caching hazelcast boot
1个回答
0
投票

此答案旨在为您的问题提供一些指导。我知道这不是一个直接的答案,因为在没有看到你的环境的情况下不可能说出一些话

  1. 假设您的二级缓存大小约为 5012 个项目。如有必要,它可能会增长到超过此大小。到期和驱逐是基于尽力而为的。
  2. 当执行 findAll() 时,它将尝试将不存在的项目放入二级缓存中。如果存在,Hibernate 将假定 L2 具有最新值并跳过此项。这就是为什么我们不应该在 Hibernate 背后更新数据库。无论如何,findAll() not L2 命中。因此,您将看到“执行... L2C 看跌期权;” 日志。作为旁注,findAll() 调用结果可以缓存在查询缓存中,但这是一个不同的主题。
  3. 当执行 findById() 时,它将尝试从二级缓存中查找该项目。这是L2命中。因此,您将看到 “正在执行...L2C 命中;” 日志

从你的日志我们大概可以推断出这一点

  • 有东西正在执行 30 个 findById() 语句。因此你有 L2 命中
  • 并且有东西正在调用 findAll()。因此,您有 L2 看跌期权,并且某些项目已经在 L2 中。
  • 最后记住 L2 是一个键值存储。只有通过按键访问才能获得命中
© www.soinside.com 2019 - 2024. All rights reserved.