Hibernate Cache для Spring Boot Application (Infinispan, Hazelcast, идеально масштабируемый) - PullRequest
0 голосов
/ 07 октября 2019

У меня есть приложение 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 в качестве спящего режимакэш. Но это не работает. Я все еще вижу те же 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 будет слишком сложен для обслуживания.

Iпонятия не имею, почему ничего не меняется (хорошо, я вижу, что 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)
}

Обновление: подключение к Management Center

Я изменил фабрику региона Hazelcast на ту, которая не является локальной для возможностиподключиться к центру управления.

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

Теперь два члена подключаются к центру управления (хотя я запускаю только одно приложение).

Статистика отображается в журналах. Я показываю толчки. Тем не менее, центр управления показывает мне, что некоторые данные были получены из кэша. Иногда в статистике я мог видеть одно попадание в кеш вместо оператора JDBC.

Ответы [ 2 ]

1 голос
/ 07 октября 2019

Если вы видите, что экземпляр Hazelcast запущен, не должно быть проблемы на стороне поставщика кэширования, и L2C должен быть готов к обслуживанию. Я думаю, что проблема в объекте вашей сущности. Сущность не будет храниться в кэше второго уровня, если вы не используете аннотацию @Cachable для класса сущности или не определите <cache ... > в entity_name.hbm.xml.

Кроме того,

... в этом случае Hibernate кэширует только аннотированные объекты?

Для кэша второго уровня - да.

У вас есть рекомендация, где мне следует вызывать статистику?

Если вы включите статистику, подробная статистика сеанса (видно из ответа Николаса) будет распечатана правильно. после закрытия сессии.

Я думаю, что Hibernate всегда будет выводить SQL, независимо от того, активируется ли кэш или нет.

Нет, запросы SQL не должны отображатьсяв журнале, если они не попадают в БД.

1 голос
/ 07 октября 2019

Я думаю, что 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)
}

Обратите внимание на попадание в кэш во втором запросе.

...