Питання 11 · Розділ 16

Як налаштувати кеш другого рівня

Налаштування кешу другого рівня включає вибір провайдера, конфігурацію Hibernate, налаштування регіонів кешу та визначення стратегій кешування для кожної сутності.

Мовні версії: English Russian Ukrainian

Огляд

Налаштування кешу другого рівня включає вибір провайдера, конфігурацію Hibernate, налаштування регіонів кешу та визначення стратегій кешування для кожної сутності.


🟢 Junior Level

Базове налаштування

Для налаштування L2 cache потрібно виконати 3 кроки:

  1. Увімкнути кеш в конфігурації
  2. Обрати провайдера (Ehcache, Hazelcast, Redis)
  3. Позначити сутності анотацією @Cacheable
# application.yml
spring:
  jpa:
    properties:
      hibernate:
        cache:
          use_second_level_cache: true
          region:
            factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
      javax.persistence.sharedCache.mode: ENABLE_SELECTIVE
@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {
    @Id
    private Long id;
    private String name;
}

Режими кешування

# Всі сутності кешуються за замовчуванням
javax.persistence.sharedCache.mode: ALL

# Тільки позначені @Cacheable (рекомендується)
javax.persistence.sharedCache.mode: ENABLE_SELECTIVE

# Всі крім позначених @Cacheable(false)
javax.persistence.sharedCache.mode: DISABLE_SELECTIVE

# Жодні (кеш вимкнений)
javax.persistence.sharedCache.mode: NONE

🟡 Middle Level

Коли НЕ використовувати L2 cache

  1. Write-heavy workload — постійна інвалідація кешу дорожча ніж SELECT з БД
  2. Високо нормалізовані дані з частими JOIN — кеш неефективний
  3. Багатовузловий кластер з частими оновленнями — invalidation traffic перевищує користь

Налаштування Ehcache

<!-- ehcache.xml в src/main/resources -->
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns='http://www.ehcache.org/v3'>

    <cache alias="com.example.User">
        <heap unit="entries">1000</heap>
        <offheap unit="MB">50</offheap>
        <expiry>
            <ttl unit="minutes">30</ttl>
        </expiry>
    </cache>

    <cache alias="com.example.Country">
        <heap unit="entries">500</heap>
        <expiry>
            <ttl unit="hours">24</ttl>
        </expiry>
    </cache>
</config>

Управління кешем програмно

Cache cache = entityManager.getEntityManagerFactory().getCache();

// Evict конкретну сутність
cache.evict(User.class, userId);

// Evict всі сутності типу
cache.evict(User.class);

// Evict все
cache.evictAll();

Колекції в кеші

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class User {

    @OneToMany(mappedBy = "user")
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private List<Order> orders;  // кешування колекції
}

Типові помилки

// ❌ Немає TTL — кеш росте нескінченно
// Рішення: завжди задавайте TTL в конфігурації провайдера

// ❌ Кеш для всіх сутностей
// Рішення: кешуйте тільки часто читані, рідко змінювані

// ❌ Неправильна стратегія
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Order { }  // ❌ Orders часто змінюються

🔴 Senior Level

Кластерний кеш

# Hazelcast для кластера
spring:
  jpa:
    properties:
      hibernate:
        cache:
          region:
            factory_class: com.hazelcast.hibernate.HazelcastCacheRegionFactory
Проблеми кластерного кешу:
- Eventual consistency (не strong) — дані в різних вузлах кластера стануть однаковими не миттєво, а через деякий час.
- Network latency при реплікації
- Конфлікти при одночасному оновленні
- Необхідність стратегії інвалідації кешу

Налаштування регіонів

@Entity
@Cacheable
@org.hibernate.annotations.Cache(
    usage = CacheConcurrencyStrategy.READ_WRITE,
    region = "userCache"  // ім'я регіону
)
public class User { }

Моніторинг в production

Statistics stats = entityManagerFactory
    .unwrap(SessionFactory.class)
    .getStatistics();

long hitCount = stats.getSecondLevelCacheHitCount();
long missCount = stats.getSecondLevelCacheMissCount();
double hitRatio = (double) hitCount / (hitCount + missCount);

// hitRatio > 0.8 зазвичай добре для read-heavy навантаження

Query Cache

spring:
  jpa:
    properties:
      hibernate:
        cache:
          use_query_cache: true
// Важливо: query cache зберігає тільки ID сутностей
// Самі сутності беруться з L2 cache

Best Practices

✅ TTL для кожного region
✅ Heap limit для запобігання memory leak
✅ Кластерний кеш для multi-node додатків
✅ Моніторинг hit ratio
✅ ENABLE_SELECTIVE mode
✅ READ_ONLY для довідників

❌ Без TTL та лімітів
❌ Кеш для даних що часто змінюються
❌ Без моніторингу hit ratio
❌ Query cache без L2 cache

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • 3 кроки налаштування: увімкнути кеш, обрати провайдера, позначити сутності @Cacheable
  • Режими: ENABLE_SELECTIVE (рекомендується), ALL, DISABLE_SELECTIVE, NONE
  • Стратегії кешування: READ_ONLY для довідників, READ_WRITE для змінюваних даних
  • Кластерний кеш: Hazelcast/Redis, eventual consistency, network overhead
  • Query cache марний без L2 cache — зберігає тільки ID сутностей

Пов’язані теми:

  • [[10. Що таке кеш другого рівня і коли його використовувати]]
  • [[9. Що таке кеш першого рівня в Hibernate]]
  • [[12. Що таке dirty checking в Hibernate]]
  • [[1. Що таке проблема N+1 і як її вирішити]]