[Redis] Redis 캐시(Cache)를 적용해 조회 성능 개선하기

저번 포스팅에서 Spring에 redis를 연동하여 redis에 데이터를 저장하는 방법을 알아보았다. 

redis 연동 연습 코드는 아래 github에서 볼 수 있다.

https://github.com/seungjjun/CodingLife/tree/main/230216

 

 

이번에는 KiCK-OFF 프로젝트에 redis cache를 적용해서 프로젝트의 성능을 개선해보려고 한다.

 

캐시(Cache)?

 그러면 캐시가 뭔데 성능을 향상할 수 있다는 것인지 알아보자.

 

캐시를 간단하게 말하면 DB에 자주 접근해서 가져오는 데이터를 임시로 저장해 둔 뒤, 이후 동일한 데이터 요청이 들어왔을 때 DB에 접근하지 않고 아까 저장해 둔 데이터를 주는 방식이다.

이러한 캐시를 이용한 서비스는 성능적인 측면에서 많은 이점을 얻을 수 있다. DB에 접근하지 않기 때문에 불필요한 쿼리 작성을 줄일 수 있기 때문이다.

 

캐시를 설명할때 대표적으로 파레토 법칙 이야기가 나오는데 파레토 법칙은 80%의 결과는 20%의 원인으로 인해 발생한다는 것을 말한다.

 

 

파레토 법칙을 서비스에 적용해 보면 서비스에도 특히 자주 사용되는 데이터가 있을 것이다. 그런 20%의 데이터에 캐싱을 적용하면 80%의 성능 향상을 기대할 수 있다. 

 

다시 말해 캐시를 아무 데이터에나 적용한다고 좋은것이 아니라, 자주 사용되는 데이터에 캐시를 적용해야 한다.

 

캐싱 적용 대상의 특징

위에서 알아본 캐싱을 다시 정의하면 데이터의 복사본을 더 빠르게 접근하기 위해서 접근하기 쉬운 저장소(redis)에 저장하여 자주 사용되는 데이터에 접근 속도를 높이는 기술이다.

 

이번에는 캐싱의 적용 대상이 되는 데이터는 어떤 특징을 갖고 있는지 알아보자.

 

1. 빈번한 접근

캐싱은 위에서 파레토 법칙에서 보았듯이 자주 사용하는 데이터에 가장 효과적이다. 자주 사용되는 데이터의 복사본을 저장함으로써 이후 데이터의 요청이 올 때마다 원본의 데이터를 주기 위해 DB에 접근하는 것을 막아 성능을 향상할 수 있다.

 

2. 데이터를 처리하기 위해 복잡한 로직이 필요한 데이터

데이터를 반환하기 위해 복잡한 로직이나, DB에서 데이터를 가져오기 위해 쿼리문이 복잡한 경우에도 캐싱을 적용하면 성능을 향상 시킬 수 있다. 캐싱을 적용하면 또 복잡한 계산을 할 필요가 없기 때문이다.

 

3. 자주 변경되지 않는 데이터

만일 데이터가 자주 업데이트가 된다면 캐싱하기 적절한 데이터가 아니다. 자주 업데이트 되는 데이터에 캐싱을 적용하면 데이터 정합성 문제가 발생하게 된다.

데이터 정합성이란?

데이터 정합성은 같은 데이터임에도 불구하고 캐시와 DB에 있는 데이터의 정보가 다른 상황을 말한다.
쉽게 예시를 들면 계좌 조회를 하는데 처음 계좌 조회를 할떄는 10,000원을 조회하였는데 1,000원을 송금하여 계좌 DB에는 9,000원이 저장이 된다. 만일 이 상황에서 또다시 계좌 조회를 하게 되면 캐시를 반환하기 때문에 사용자는 10,000원을 조회하게 된다.

즉, 10,000(캐시) != 9,000(DB) 데이터가 일치하지 않는 데이터 정합성 문제가 발생하게 된다.

 

위와 같은 문제 때문에 자주 변경이 되지 않는 데이터에 캐싱을 적용해야 한다. 

 

캐싱 적용하기

이제 캐싱을 프로젝트에 적용해보자.
나는 어떤 데이터에 캐싱을 적용하는 것이 좋을까 생각해 보았는데, 인기 게시글을 요청하는 메서드에 캐싱을 적용하면 괜찮을 것 같다는 생각을 했다.

 

게시판의 특성상 게시글을 요청하는 일이 많고, 특히 인기 검색어는 어떤 카테고리의 게시판을 접근하더라도 무조건 동일한 데이터를 요청하게 되어있기 때문에 가장 자주 요청하는 데이터였고,  인기 게시글의 경우 조회수를 기반으로 선정이 되는데, 슬프지만 트래픽이 많이 발생하지 않는 나의 프로젝트 경우 인기 게시글이 변경될 일이 적기 때문에 적절한 데이터라고 생각을 하고 적용을 해보았다.

 

우선 Spring에 Redis 연동하는 방법이나 Config 설정 방법은 이전 포스팅에서 공부해 보았으니 이번 포스팅에서는 생략하겠다.

 

이전 포스팅에서 다루지 않은 @Cacheable을 적용해 보자

 

인기 게시글 데이터에 캐싱을 적용한다 했으니 인기 게시글을 Get 해오는 메서드에 @Cacheable 어노테이션을 추가해 주면 된다.

@Cacheable(value = "hotPostsCache", cacheManager = "redisCacheManager")
public HotPostsDto hotPosts() {
    // ...
}

 

위 예시처럼 메서드 위에 @Cacheable 어노테이션을 추가해준 후 value = "hotPostsCache", cacheManager = "redisCacheManager"라고 지정을 해주었는데 하나씩 알아보자.

 

value값은 hotPosts 메서드가 반환하는 데이터가 저장될 캐시의 이름을 정의하는 데 사용이 된다.

즉 redis에서 인기 게시글 데이터는 hotPostsCache라는 이름의 캐시를 사용하도록 하는 것이다.

 

cacheManager는 RedisConfig 클래스 파일에서 Bean으로 등록한 RedisCacheManager이름을 지정해주어야 한다.

나의 RedisConfig 클래스 파일에서 RdisCacheManager는 아래와 같이 Bean등록을 해주었다.

public class RedisConfig { 
    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
            .entryTtl(Duration.ofMinutes(30L));

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

 

추가로 enrtyTtl을 이용해 캐시의 유효기간을 30분으로 설정해 주었다.

 

위와 같이 캐싱을 적용하고 다시 배포를 해주었다. 

 

캐시 성능 개선 확인하기

우선 캐시 적용 전 인기 게시글 조회 성능을 확인해 보았다.

 

성능 테스트는 Postman과 JMeter를 이용하였다.

 

 

캐시 적용 전

우선 캐시를 적용하기 전 응답 속도를 측정해 보았다.

 

먼저 postman으로 인기 게시글 api호출을 10번 정도 했을 때 평균적으로 500ms 속도가 걸리는 것을 확인할 수 있었다.

 

Postman

JMeter는 아래와 같이 설정을 해주고 테스트를 해보았다.

10개의 Thread를 생성하고 Thread의 반복 횟수를 100으로 설정해 총 1000번 api 요청을 하도록 설정해 주었다.

 

Response Times Over Time

응답 속도가 일정하지는 않지만 평균적으로 500ms로 응답하는 것을 확인할 수 있었다.

 

캐시 적용 후 

캐시 적용 후 성능 테스트를 진행한 결과이다.

 

이번에도 먼저 postman을 이용해 10번 정도 호출을 했을 때 평균적으로 250ms ~ 300ms 사이로 응답하는 것을 확인할 수 있었다.

 

Postman

 

JMeter 위와 동일한 설정으로 테스트를 진행해 보았다.

 

Response Times Over Time

 

이번에는 저번 테스트와 달리 중간에 한번 튄 것을 빼고는 평균적으로 270ms으로 응답하는 것을 확인할 수 있다.

 

확실히 캐시를 적용한 이후에 응답속도가 빨라진 것을 확인할 수 있었다.

 

어느 정도 성능을 개선했는지 %로 수치화 했을 때 ((270ms - 500ms) / 500ms) * 100 = -46%이라는 결과가 나온다.

즉, 캐시를 적용함으로써 약 46% 성능 개선을 했다고 볼 수 있다.

 

 

전체적인 프로젝트 코드는 아래에서 확인할 수 있다.

https://github.com/seungjjun/kick-off-backend

 

Reference

JMeter 사용법: https://effortguy.tistory.com/164

https://loosie.tistory.com/807