Как синхронизировать кэш между несколькими экземплярами сервиса (контейнерами)?

«Как синхронизировать кэш между несколькими экземплярами сервиса (контейнерами)?» — вопрос из категории Архитектура, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Проблема «пустого кэша» возникает, когда каждый экземпляр сервиса имеет свой изолированный локальный кэш. Решение — использовать распределенный (общий) кэш или стратегию инвалидации.

Основные подходы:

  1. Внешнее распределенное хранилище кэша (Рекомендуется). Все экземпляры сервиса подключаются к одному кэш-серверу.

    • Redis: Самый популярный вариант. Spring Boot поддерживает через spring-boot-starter-data-redis.
      
      // application.properties
      spring.cache.type=redis
      spring.redis.host=shared-redis

    // Конфигурация @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { return RedisCacheManager.create(factory); }

    
    - **Memcached, Hazelcast, Apache Ignite.**
  2. Событийная синхронизация (Cache Invalidation). При обновлении данных в одном экземпляре он рассылает событие другим для очистки (evict) их локального кэша. Можно использовать брокеры сообщений (Kafka, RabbitMQ) или Spring Cloud Bus.

  3. Стратегия «Write-Through» / «Cache-Aside» с общей БД. Кэш используется только для чтения. Запись всегда идет в основную базу данных. При этом:

    • Устанавливается короткий TTL (Time To Live) для записей кэша, чтобы данные периодически обновлялись.
    • Используется общая БД как источник истины. Риск: кратковременная несогласованность (пока TTL не истек).
  4. Клиентские библиотеки с распределенной поддержкой. Например, Caffeine + JCache (JSR-107) провайдер, такой как Hazelcast, который может автоматически реплицировать изменения кэша между узлами.

Практический совет: Для большинства микросервисных архитектур Redis является стандартным выбором для распределенного кэширования из-за простоты, производительности и широкой поддержки.