Spring/Spring Boot

Spring Boot 에서 Redis Cache 사용하기

민돌v 2024. 7. 2. 23:30

이번 포스팅에서는 이전 포스팅에서 못 다한 Spring Redis Cache 를 사용해보고자 합니다.
코드는 Github 에 있습니다.

💡Redis 묶음

 

 

[목차]

  1. Cache Manager
  2. Redis Cache
    1. config
    2. @Cachable
    3. @CachEvict
  3. Cache hits 모니터링 종류

Spring Cache Abstraction

Spring 에서는 캐시 기능 자체에 대해서 특정 기술에 종속되지 않게 추상화를 제공합니다.

AOP 를 이용한 어노테이션을 활용하여 특정 기술에 종속되지 않고, 애플리케이션에 캐싱 기능을 부여할 수 있고
이를 통해 프로덕션 코드가 외부 서비스의 변경에 의존하지 않도록 해줍니다.

따라서 직접적으로 캐싱기능을 제공하는 외부 서비스를 Cache Manager 로 Bean 으로 등록하여 사용할 수 있습니다.


Spring Cache Manager

캐시 추상화 에서는 캐시 기술을 지원하는 캐시 매니저를 빈으로 등록해야합니다.
캐시 매니저의 종류는 상당히 여러가지인데, 저는 Redis 를 캐시매니저로 사용해보고자 합니다.

 

그 외의 캐시 매니저

  1. ConcurrentMapCacheManager
  2. SimpleCacheManager
  3. EhCacheCacheManager
  4. CaffeineCacheManager .. 등등 

✔️ 요즘은 CaffeineCacheManager 가 EnCache 보다 성능이 좋다고하여 잘 쓰인다고 합니다

 


Redis Cache

  • 레디스는 Cache Manager 로 굉장히 많이 사용됩니다.
  • Cache 란 데이터를 더 빠르게 조회하기 위해, 한번 조회된 데이터를 임시 저장소에 저장한 후 → 반복된 동일 조회 시 임시 저장소에서 해당 데이터를 빠르게 호출해오는 것을 의미합니다.
  • 레디스는 In-memory를 사용하는 서버이기 때문에, Cache 저장소로 충분히 사용할만큼 빠른 작업 속도를 가집니다.

 

0) Yaml

Spring 에서 Cache 용도로 Redis 를 쓸거라고 명시적으로 작성해줍니다.

spring:
  cache:
    type: redis

 

1) Config

그 다음 RedisCacheManager 를 설정해줍니다.

@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {

        return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        return RedisCacheManager.create(connectionFactory);
    }
}

✔️ RedisCacheManagerBuilder

  • 혹은 RedisCacheManagerBuilder로 구성할 수 있으며, 이를 통해 기본 RedisCacheConfiguration, 트랜잭션 동작 및 미리 정의된 캐시를 설정할 수도 있습니다.
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {

        return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {

        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(defaultCacheConfig())
            .withInitialCacheConfigurations(singletonMap("predefined", defaultCacheConfig().disableCachingNullValues()))
            .transactionAware()
            .build();

//        return RedisCacheManager.create(connectionFactory);
    }
}

 

✔️ RedisCacheConfiguration

  • RedisCacheManager로 생성된 RedisCache의 동작은 RedisCacheConfiguration으로 정의됩니다.
  • 이 구성을 사용하면 다음 예제와 같이 바이너리 스토리지 형식으로 변환하기 위한 키 만료 시간, 접두사 및 RedisSerializer 구현을 설정할 수 있습니다.
  • 이 외에 읽기 쓰기 잠금, 배치 전략, 만료시간 설정, 통계 등의 설정은 공식문서를 참고해주세요
@Configuration
@EnableCaching
public class RedisConfig {

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {

        return new LettuceConnectionFactory(new RedisStandaloneConfiguration("localhost", 6379));
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .disableCachingNullValues()
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .entryTtl(Duration.ofMinutes(3L));


        return RedisCacheManager
            .RedisCacheManagerBuilder
            .fromConnectionFactory(connectionFactory)
            .cacheDefaults(redisCacheConfiguration)
            .build();
    }
}

 


2) 캐싱 사용하기

Spring 에서 제공하는 3가지 어노테이션으로 캐싱 작업을 설정할 수 있습니다.

  • @Cacheable : 메서드 호출 시, 메서드가 호출된 적 있는지를 판단하고 있으면 캐싱된 데이터를 리턴, 없으면 리턴값을 캐싱
  • @CachePut : 데이터를 캐시에 저장
  • @CacheEvict : 캐시 삭제

@Cacheable

  • 호출 결과(리턴 값)을 캐시할 수 있음을 의미하는 주석입니다.
  • cacheName(value) 값을 기준으로 캐시를 저장합니다.
  • 해당 메서드가 호출될 때 마다 캐싱 동작이 수행되며 해당 메서드가 이미 호출되어 있는지 여부를 확인하고 캐시 데이터를 호출합니다.
@Cacheable(cacheNames = "findTicketAll", key = "#root.methodName", cacheManager = "cacheManager")
public List<Ticket> getTickets() {
    return ticketRepository.findAll();
}

5만개 조회 229ms

캐싱후 8ms
Redis 에 생성된 Key 와 Value

✔️  Optional Element

  • cacheName : 캐시 이름 
  • value : cacheName의 alias (cacheName 을 쓰나, value 를 쓰나 저장되는 key 값은 같았습니다.)
  • key : 동적인 키 값을 사용하는 SpEL 표현식 (동일한 cache name을 사용하지만 구분될 필요가 있을 경우 사용되는 값)
  • condition : SpEL 표현식이 참일 경우에만 캐싱 적용 (or, and 등 조건식, 논리연산 가능)
  • unless : 캐싱을 막기 위해 사용되는 SpEL 표현식 (condition과 반대로 참일 경우에만 캐싱이 적용되지 않음)
  • cacheManager : 사용 할 CacheManager 지정 
  • sync
    • 여러 스레드가 동일한 키에 대한 값을 로드하려고 할 경우, 기본 메서드의 호출을 동기화
    • 즉, 캐시 구현체가 Thread safe 하지 않는 경우, 캐시에 동기화를 걸 수 있는 속성
@Cacheable(cacheNames = "platformTeamBooks", key = "#root.target + #root.methodName + '_'+ #p0")
public Book getPlatformTeamBook(int bookId) { /* ... */}

@Cacheable(cacheNames = "users", condition = "#user.type == 'ADMIN'")
public Book selectUser(User user) { /* ... */}

@Cacheable(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}

출처: https://gngsn.tistory.com/157 [ENFJ.dev:티스토리]

 


@CacheEvict

  • 메서드가 호출될 때 캐시에 있는 데이터가 삭제합니다.
  • ✔️ 데이터가 변경됬을 시, 캐싱 된 데이터가 함께 변경되지 않기 때문에 보통 캐싱되었던 데이터를 삭제할 때 사용합니다.

✔️  Optional Element

  • allEntries : 캐시 내의 모든 리소스를 삭제할지의 여부 (defalut : false)
  • condition : SpEL 표현식이 참일 경우에만 삭제 진행 ( or, and 등 조건식, 논리연산 가능 )
  • beforeInvocation
    • true - 메서드 수행 이전 캐시 리소스 삭제 
    • false - 메서드 수행 후 캐시 리소스 삭제
@CacheEvict(cacheNames = "findTicketAll", allEntries = true, cacheManager = "cacheManager")
public void updateTicketQuantity(long ticketId) {

    Ticket ticket = ticketRepository.findById(ticketId).get();
    ticket.changeQuantity(-1);

    ticketRepository.save(ticket);
}

 


@CachePut

  • 캐시에 값을 저장하는 용도로만 사용합니다.
  • @Cacheabler 과 다르게 데이터를 무조건 저장하며 항상 메소드의 로직을 실행합니다.
 

 


3) Redis Cache Hits 모니터링

👏 캐시의 성능을 측정하는 데 사용되는 몇 가지 용어가 있습니다. 이 용어들은 캐시의 효율성과 성능을 평가하고 측정하는 데에 도움이 됩니다.

  • Cache Hit (캐시 적중): 캐시에 데이터가 이미 존재하여 캐시를 통해 데이터 제공을 수행한 경우.
  • Cache Miss (캐시 누락): 접근하려는 데이터에 캐싱되어 있지 않은 상태. 이런 경우 원본 서버/스토리지를 통해 데이터를 조회가 이뤄진다.
  • Cache Hit Rate(캐시 적중률): 데이터를 캐시로 제공을 수행한 비율.
  • Cache Hit Ratio = Cache Hits Count / (Cache Hits Count + Cache Misses Count)

 

캐싱 성능이 잘나온다는 건 높은 Hit Ratio ↑, 낮은 Miss Ratio 를 말합니다.

 

✔️ 모니터링 도구

  1. Redis stat
  2. grafana
  3. spring actuator

 

이렇게 3가지 정도가 Redis Hit Ratio 를 모니터링하기 좋은 도구인 것 같습니다.

→ 간단하게 Redis Server 의 자원 사용량에 대해 모니터링하기하는 방법으로는 'INFO ALL' 명령어도 존재합니다

info all

 

 

Spring Boot

Redis Cache 에 대하여 

 

 

끝..!

 

 


참고