← назад к разделу

Spring Boot умеет работать с Redis «из коробки» — достаточно добавить зависимость, указать адрес и выбрать нужный инструмент: ручные операции через RedisTemplate, автоматический кэш через @Cacheable или хранение сессий через Spring Session.

Подключение и клиент Lettuce

По умолчанию Spring Data Redis использует клиент Lettuce — асинхронный, потокобезопасный, основанный на Netty. Добавляем зависимость:

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

Настройка в application.yml:

spring:
  data:
    redis:
      host: localhost
      port: 6379
      # password: secret   # если включена аутентификация
      # database: 0        # номер логической базы (0 по умолчанию)
      lettuce:
        pool:
          max-active: 8
          max-idle: 4

Spring Boot автоматически создаёт LettuceConnectionFactory и RedisTemplate<String, Object>. Переопределять бин подключения нужно только при нестандартной конфигурации (кластер, Sentinel, TLS).

RedisTemplate и StringRedisTemplate

RedisTemplate — универсальный инструмент для прямых операций с Redis. Он предоставляет «операции» по типу структуры данных:

@Service
public class CounterService {

    private final StringRedisTemplate redis;

    public CounterService(StringRedisTemplate redis) {
        this.redis = redis;
    }

    public void increment(String key) {
        redis.opsForValue().increment(key);
        redis.expire(key, Duration.ofHours(1));
    }

    public Long get(String key) {
        String value = redis.opsForValue().get(key);
        return value != null ? Long.parseLong(value) : 0L;
    }
}

StringRedisTemplate — это RedisTemplate<String, String> со строковой сериализацией. Подходит, когда и ключ, и значение — строки.

Операции по типу структуры:

МетодТип данных Redis
opsForValue()String (скалярное значение)
opsForHash()Hash
opsForList()List
opsForSet()Set
opsForZSet()Sorted Set

Грабля: сериализация по умолчанию

Если использовать RedisTemplate<String, Object> без настройки, значения сохраняются через JdkSerializationRedisSerializer. Данные в Redis выглядят как нечитаемый байтовый поток и привязаны к конкретным Java-классам — при изменении класса десериализация сломается.

Правильный подход — настроить JSON-сериализацию:

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(
            mapper.getPolymorphicTypeValidator(),
            ObjectMapper.DefaultTyping.NON_FINAL
        );

        Jackson2JsonRedisSerializer<Object> serializer =
            new Jackson2JsonRedisSerializer<>(mapper, Object.class);

        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        return template;
    }
}

После этого значения в Redis хранятся как читаемый JSON.

Spring Cache поверх Redis

Spring Cache — абстракция над кэшем. Декорируешь метод аннотацией, Spring сам решает: сходить ли в базу или вернуть закэшированный результат.

Зависимость и включение

implementation 'org.springframework.boot:spring-boot-starter-cache'
@SpringBootApplication
@EnableCaching
public class Application { ... }

@Cacheable, @CacheEvict, @CachePut

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        // вызывается только при отсутствии в кэше
        return repository.findById(id).orElseThrow();
    }

    @CacheEvict(value = "products", key = "#product.id")
    public void update(Product product) {
        repository.save(product);
        // кэш для этого ключа сбрасывается
    }

    @CachePut(value = "products", key = "#result.id")
    public Product create(Product product) {
        return repository.save(product);
        // метод всегда выполняется, результат записывается в кэш
    }

    @CacheEvict(value = "products", allEntries = true)
    public void invalidateAll() {
        // удалить весь кэш products
    }
}

RedisCacheManager и TTL

По умолчанию кэш живёт бесконечно. Задать TTL можно через RedisCacheManager:

@Configuration
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration defaults = RedisCacheConfiguration
            .defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair
                    .fromSerializer(new GenericJackson2JsonRedisSerializer())
            );

        Map<String, RedisCacheConfiguration> cacheConfigs = Map.of(
            "products", defaults.entryTtl(Duration.ofHours(1)),
            "sessions", defaults.entryTtl(Duration.ofMinutes(5))
        );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaults)
            .withInitialCacheConfigurations(cacheConfigs)
            .build();
    }
}

Короткая формула: RedisCacheConfiguration — это настройка одного кэша; RedisCacheManager собирает их вместе и регистрирует в Spring.

Сессии через Spring Session

Spring Session переносит пользовательские HTTP-сессии из памяти приложения в Redis. Это даёт две вещи:

  1. Горизонтальное масштабирование — несколько экземпляров приложения работают с общим хранилищем сессий, пользователь не теряет сессию при переключении между ними.
  2. Переживание перезапуска — сессии не исчезают при рестарте приложения.

Зависимость:

implementation 'org.springframework.session:spring-session-data-redis'

В application.yml:

spring:
  session:
    store-type: redis
    timeout: 30m
    redis:
      flush-mode: on-save    # записывать сессию только при изменении
      namespace: "myapp:session"

Больше ничего настраивать не нужно — Spring Boot автоматически создаёт RedisIndexedSessionRepository и подключает фильтр SessionRepositoryFilter. Существующий код с HttpSession работает без изменений.

Как это выглядит в Redis

После запуска приложения с @Cacheable и Spring Session ключи в Redis будут выглядеть примерно так:

# Кэш через @Cacheable
> KEYS products::*
1) "products::42"
2) "products::17"

> GET "products::42"
"{\"id\":42,\"name\":\"Widget\",\"price\":9.99}"

> TTL "products::42"
(integer) 3542   # секунд до истечения

# Сессии через Spring Session
> KEYS myapp:session:*
1) "myapp:session:sessions:a3f9c..."
2) "myapp:session:sessions:b12ef..."

Коротко

  • Lettuce — клиент по умолчанию в Spring Boot; потокобезопасный, настраивается в application.yml.
  • StringRedisTemplate — для строковых операций; RedisTemplate<String, Object> — для сложных значений.
  • Сериализацию менять обязательно: заменяй JdkSerializationRedisSerializer на GenericJackson2JsonRedisSerializer.
  • @Cacheable / @CacheEvict / @CachePut — декларативный кэш; @EnableCaching включает механизм.
  • RedisCacheManager настраивает TTL отдельно для каждого кэша.
  • Spring Session переносит HTTP-сессии в Redis — масштабирование без потери сессий.

Что почитать дальше

  • Основы Redis — структуры данных, TTL, персистентность
  • Паттерны кэширования — cache-aside, read-through, write-through, когда что применять
  • Эксплуатация Redis — мониторинг, репликация, Sentinel