用于Spring Boot应用程序的Hibernate Cache(Infinispan,Hazelcast,理想情况下可扩展)

时间:2019-10-07 11:49:04

标签: hibernate spring-boot caching hazelcast infinispan

我有一个Spring Boot应用程序,它通过Hibernate检索(并存储)数据。 Hibernate已连接到MySQL数据库。在从数据库中检索某些表之前,我使用一些实体图对数据库请求进行了一些优化,以加入一些表。现在,我希望将最常见的对象(不会经常更改)存储在缓存中。

我尝试通过Infinispan和Hazelcast进行缓存。我在这里和那里更改了配置,但是总是以某种方式从数据库中检索实体。

对于Halzelcast,我在pom.xml中添加了以下三个依赖项。即使hazelcast-hibernate似乎已集成在hazelcast-spring中,我也需要第三个依赖项,因为否则,区域工厂将不可用。

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
    <version>3.12.2</version>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
    <version>3.12.2</version>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-hibernate53</artifactId>
    <version>1.3.2</version>
</dependency>

以下设置对于使用Hazelcast作为Hibernate缓存应该足够了。但这行不通。我仍然在日志中看到与以前相同的SQL查询。

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory

应使用HazelcastLocalCacheRegionFactory,因为性能应稍好一些,并且ram(当前)不是一个大问题。不过,我也欢迎采用其他方法。

相关实体同时获得两个注释(@Cacheable@Cache)。

@Entity(name = "business_units")
@Getter @Setter
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class BusinessUnit extends Auditable<String> implements Serializable {

    private static final long serialVersionUID = -6994142588281279518L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
    @GenericGenerator(name = "native", strategy = "native")
    @Column(nullable = false, name = "id")
    private Integer id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "allocated_responsibility_id", referencedColumnName = "id")
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private ProductManager allocatedResponsibility;

    // Some other attributes, getters and setters...

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof BusinessUnit)) return false;
        BusinessUnit that = (BusinessUnit) o;
        return id != null && id.equals(that.id);
    }

    @Override
    public int hashCode() {
        return 31;
    }
}

Infinispan也尝试了相同的方法(不同的依赖关系,但相同的不存在结果)。

我打算使用外部提供商,因为该应用程序应可扩展。如果不注意这一点,缓存将无法再正确镜像数据库。

应该使用嵌入式缓存,因为用于Hazelcast或Infinispan的额外服务器太难维护了。

我不知道为什么什么都没有改变(好的,我看到Hazelcast开始了)。我的另一个想法是使用查询缓存而不是Hibernate缓存。但这需要更多注意以确保同步数据库和缓存。此外,该应用可能会从中受益较少。

能否请您告诉我为什么它不起作用以及需要对其进行哪些更改?

更新:添加统计信息

我尝试了以下建议,并添加了@Nicolas建议的统计信息。

spring.jpa.properties.hibernate.generate_statistics=true

当我第一次加载这样的页面时,数据会从数据库中请求并放入缓存(L2C)。

1519200 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
1759800 nanoseconds spent preparing 1 JDBC statements;
4144000 nanoseconds spent executing 1 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
161900 nanoseconds spent performing 3 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

但是当我再次访问该站点(或执行任何其他应访问高速缓存的操作)时,数据再次从数据库中请求(并放入高速缓存中)。

再次访问该页面时的日志:

2019-10-08 12:51:38.399  INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
    0 nanoseconds spent acquiring 0 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    0 nanoseconds spent preparing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}
2019-10-08 12:51:38.404 DEBUG 17028 --- [nio-8080-exec-8] org.hibernate.SQL                        : 
    select
        businessun0_.id as id1_0_0_,
        productman1_.id as id2_9_1_,
        employee2_.id as id1_4_2_,
        businessun0_.created_by as created_2_0_0_,
        businessun0_.created_date as created_3_0_0_,
        businessun0_.last_modified_by as last_mod4_0_0_,
        businessun0_.last_modified_date_time as last_mod5_0_0_,
        businessun0_.allocated_responsibility_id as allocate7_0_0_,
        businessun0_.name as name6_0_0_,
        productman1_.employee_id as employee3_9_1_,
        employee2_.created_by as created_2_4_2_,
        employee2_.created_date as created_3_4_2_,
        employee2_.last_modified_by as last_mod4_4_2_,
        employee2_.last_modified_date_time as last_mod5_4_2_,
        employee2_.email_address as email_ad6_4_2_,
        employee2_.location as location7_4_2_,
        employee2_.name as name8_4_2_,
        employee2_.team as team9_4_2_ 
    from
        business_units businessun0_ 
    left outer join
        responsibilities productman1_ 
            on businessun0_.allocated_responsibility_id=productman1_.id 
    left outer join
        employees employee2_ 
            on productman1_.employee_id=employee2_.id
2019-10-08 12:51:38.412  INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
    901000 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    1075000 nanoseconds spent preparing 1 JDBC statements;
    1766400 nanoseconds spent executing 1 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    166800 nanoseconds spent performing 3 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

更新:连接到管理中心

我将Hazelcast的地区工厂更改为不能够连接管理中心的本地工厂。

spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastCacheRegionFactory

现在,有两个成员连接到管理中心(即使我仅启动一个应用程序)。

日志中显示的统计信息仍显示推送。但是,管理中心向我显示已从缓存中检索了一些数据。有时,我可以在统计信息中看到一个缓存命中而不是JDBC语句。

2 个答案:

答案 0 :(得分:1)

我认为,无论缓存是否激活,SQL都将始终由Hibernate输出。如果要确保已使用缓存,请激活Hibernate统计信息:

spring.jpa.properties.hibernate.generate_statistics=true

在第一个请求上,它应该输出如下内容:

Session Metrics {
    388816 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    2436908 nanoseconds spent preparing 1 JDBC statements;
    2585533 nanoseconds spent executing 1 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    10276363 nanoseconds spent performing 1 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    54012289 nanoseconds spent performing 1 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

在以后的请求中,类似:

Session Metrics {
    79940 nanoseconds spent acquiring 1 JDBC connections;
    0 nanoseconds spent releasing 0 JDBC connections;
    0 nanoseconds spent preparing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    1665675 nanoseconds spent performing 1 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
    0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
}

请注意第二个请求上的缓存命中。

答案 1 :(得分:1)

如果您看到Hazelcast实例已启动,则在缓存提供程序端应该没有问题,并且L2C必须已准备就绪可以服务。我认为问题出在您的实体对象上。除非您为实体类使用@Cachable注释或在<cache ... >中定义entity_name.hbm.xml,否则实体将不会保留在二级缓存中。

  

...在这种情况下,Hibernate是否仅缓存带注释的实体?

对于二级缓存,是的。

  

您对我应该给统计数据打电话的地方有建议吗?

如果启用统计信息,则在会话关闭后,将立即打印出详细的会话统计信息(有关Nicolas的回答)。

  

我认为无论缓存是否激活,Hibernate都将始终输出SQL。

否,除非SQL查询命中数据库,否则不应在日志中显示。