Spring Boot: Redis를 사용하여 embedded cache 구현하기
Cache란?
나중에 할 요청 결과를 미리 저장 해 두었다가 빠르게 서비스 해 주는 것
- Look aside Cache(일반적으로 많이 쓰는 방식)
- DB를 가기 전에 Cache에 먼저 데이터가 있는지 확인 해본다.
- 만약 Cache에 데이터가 있다면 캐시 데이터를 읽고, 없다면 DB를 읽은 후 Cache에 저장 & 결과 리턴
- Write Back(로그를 DB에 저장하는 경우, 쓰기 연산이 많은 경우)
- 캐시에 먼저 저장했다가, 특정 시점마다 DB에 저장하는 방식
- ex. 걸리는 시간(배치 작업) - DB insert 쿼리를 1번씩 * 500번 >> insert쿼리 500개 합친 것 * 1번
- 단점: 장애가 생기면 데이터가 사라질 위험이 있다. 왜냐면 리부팅 되면 메모리는 사라지기 때문
캐시의 대상이 되는 정보들은 아래와 같다.
- 단순한, 또는 단순한 구조의 정보를 -> 정보의 단순성
- 반복적으로 동일하게 제공해야 하거나 -> 빈번한 동일요청의 반복
- 정보의 변경주기가 빈번하지 않고, 단위처리 시간이 오래걸리는 정보이고 -> 높은 단위처리비용
- 정보의 최신화가 반드시 실시간으로 이뤄지지 않아도 서비스 품질에 영향을 거의 주지 않는 정보
- 예) 실시간 검색어, 방문자수/조회수/추천수, 1회성 인증정보, 공지사항/Q&A 등
Redis란?
REDIS(REmote Dictionary Server)는 메모리 기반의 “키-값” 구조 데이터 관리 시스템이며, 모든 데이터를 메모리에 저장하고 조회하기에 빠른 Read, Write 속도를 보장하는 비 관계형 데이터베이스이다.
Redis가 타 캐시 시스템(ex. MemCache 등)과 다른 특징은 아래와 같다.
|
Embedded Redis를 이용하여 @Service 메소드를 캐시 함수로 사용하기
1. gradle 추가
implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'it.ozimov:embedded-redis:0.7.2'
2. config class 추가 - 주의
spring:
profiles:
active: local
cache:
type: redis
redis:
host: localhost
port: 6375
public class CacheKey {
public static final int DEFAULT_EXPIRE_SEC = 600; // 10 minutes
public static final String STOCK = "stock";
public static final int STOCK_EXPIRE_DAY = 1;
}
@Profile("local")
@Configuration
public class EmbeddedRedisConfig {
@Value("${spring.redis.port}")
private int redisPort;
private RedisServer redisServer;
@PostConstruct
public void redisServer() {
redisServer = new RedisServer(redisPort);
redisServer.start();
}
@PreDestroy
public void stopRedis() {
if (redisServer != null) {
redisServer.stop();
}
}
}
@RequiredArgsConstructor
@EnableCaching
@Configuration
public class RedisConfig {
private final ObjectMapper objectMapper;
@Autowired
public RedisConfig() {
this.objectMapper = new ObjectMapper();
}
@Bean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
var jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(StockResult.class);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofSeconds(CacheKey.DEFAULT_EXPIRE_SEC))
.computePrefixWith(CacheKeyPrefix.simple())
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(
jackson2JsonRedisSerializer
));
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
cacheConfigurations.put(CacheKey.STOCK, RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(CacheKey.STOCK_EXPIRE_DAY)));
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory).cacheDefaults(configuration)
.withInitialCacheConfigurations(cacheConfigurations).build();
}
}
여기서 주의해야 할 점은 나는 레디스에 저장 할 값이 객체이기 때문에 Serializer를레디스에서 제공해 주는 jackson2JsonRedisSerializer를 사용했다. 이 때, 아래와 같이 ObjectMapper가 StockResult라는 객체를 Serialize 해 줄 수 있도록 코드를 추가한다.
var jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(StockResult.class);
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
3. @Service 메소드에 @Cacheable 추가하기
@Cacheable(value = CacheKey.STOCK, key = "#stockRequestDto.stockSymbol+#stockRequestDto.dateToday", unless = "#result == null")
public StockResult calculateStockResult(StockRequestDto stockRequestDto) {
...
}
옵션의 value에는 저장시 키값을, key에는 키 생성시 추가로 덧붙일 파라미터 정보를 선언한다.
나의 경우 캐시키는 stock::AAPL2020-09-27과 같은 형태로 생성된다.
unless = “#result == null”는 메서드 결과가 null이 아닌 경우에만 캐싱하도록 하는 옵션이다.
4. 메인 Application에 @EnableCaching 어노테이션 추가하기
@EnableCaching
@SpringBootApplication
public class StockApplication {
public static void main(String[] args) {
SpringApplication.run(StockApplication.class, args);
}
}
이렇게 만들어 본 후 포스트맨으로 테스트 해 보니,
맨 처음에 캐시에 데이터가 없는 경우를 제외하고는 결과 값을 바르게 바로바로 리턴하는 것을 확인할 수 있었다.
참고
https://www.youtube.com/watch?v=mPB2CZiAkKM
https://daddyprogrammer.org/post/3870/spring-rest-api-redis-caching/
SpringBoot2로 Rest api 만들기(15) – Redis로 api 결과 캐싱(Caching)처리
이번 장에서는 지금까지 개발한 api에 캐시를 적용해 보도록 하겠습니다. 캐시란 자주 사용되는 데이터를 메모리에 저장하고 반환하여 하드디스크의 원본데이터를 거치지 않게 함으로서 리소스
daddyprogrammer.org
https://sabarada.tistory.com/103
[Redis] 캐시(Cache)와 Redis
[Redis] 캐시(Cache)와 Redis [Redis] Redis의 기본 명령어 [Java + Redis] Spring Data Redis로 Redis와 연동하기 - RedisTemplate 편 [Java + Redis] Spring Data Redis로 Redis와 연동하기 - RedisRepository..
sabarada.tistory.com
SpringBoot2로 Rest api 만들기(15) – Redis로 api 결과 캐싱(Caching)처리
이번 장에서는 지금까지 개발한 api에 캐시를 적용해 보도록 하겠습니다. 캐시란 자주 사용되는 데이터를 메모리에 저장하고 반환하여 하드디스크의 원본데이터를 거치지 않게 함으로서 리소스
javaengine.tistory.com