강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

이수현님의 프로필 이미지
이수현

작성한 질문수

비전공자도 이해할 수 있는 Redis 입문/실전 (조회 성능 최적화편)

Redis를 적용했음에도 불구하고 조회 성능의 큰 차이가 없고 오히려 더 안좋은 조회속도를 보이는 것 같습니다.

작성

·

53

·

수정됨

0

간단한 영화 목록 조회입니다. 총 약 10000개의 데이터가 있습니다. redis를 적용하고 조회성능이 좋아진것은 맞지만 이상하게 redis를 적용하기 전에도 같은 조회를 계속해도 조회속도가 똑같이 빨라지는 현상을 겪고있습니다. 현재 이 movie는 MSA 의 여러 서비스 중 하나입니다. 혹시 MSA 아키텍쳐 자체가 문제가 될 수도 있나요?

@Cacheable(cacheNames = "getMovies", key = "'movie:all'",
        cacheManager = "movieCacheManager")
public List<Movie> getAllMovies() {
    return movieRepository.findAll();
}


@GetMapping("/movies")
    public ResponseEntity<List<MovieDto>> getAllMovies() {
        List<Movie> allMovies = movieService.getAllMovies();
        List<MovieDto> dtos = new ArrayList<>();
        for (Movie movie : allMovies) {
            dtos.add(new ModelMapper().map(movie, MovieDto.class));
        }

        return ResponseEntity.status(HttpStatus.OK).body(dtos);
    }


@Configuration
@EnableCaching // Spring Boot의 캐싱 설정을 활성화
public class RedisCacheConfig {

    @Bean
    public CacheManager movieCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                .defaultCacheConfig()
                // Redis에 Key를 저장할 때 String으로 직렬화(변환)해서 저장
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new StringRedisSerializer()))
                // Redis에 Value를 저장할 때 Json으로 직렬화(변환)해서 저장
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new GenericJackson2JsonRedisSerializer()
                        )
                )
                // 데이터의 만료기간(TTL) 설정(1분)
                .entryTtl(Duration.ofMinutes(1L));
        return RedisCacheManager
                .RedisCacheManagerBuilder
                .fromConnectionFactory(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }

}

@Configuration
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private int port;

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
    }

}

답변 3

0

JSCODE 박재성님의 프로필 이미지
JSCODE 박재성
지식공유자

안녕하세요 ! 질문 잘 해주셨어요 !

질문해 주신 내용에 대해 상황을 기반으로 하나씩 정리해서 답변드려 볼게요 !

 

[1] JPA 자체의 1차 캐시(영속성 컨텍스트) 때문일 가능성

우선 가장 유력한 원인이라고 생각됩니다 !

"movieRepository.findAll()이 반복 호출임에도 DB 부하 없이 빠른 이유"

Spring Data JPA는 기본적으로 영속성 컨텍스트(EntityManager)가 존재하며,

트랜잭션 안에서 같은 데이터를 여러 번 조회할 경우 DB를 다시 조회하지 않고

1차 캐시에 보관된 엔티티를 반환해요~

즉, Redis를 사용하지 않아도 같은 요청이 계속 반복된다면,

이미 JPA가 빠른 결과를 리턴해줬던 것일 수 있어요!

 

[2] 너무 많은 10,000개 영화 데이터를 한번에 캐싱

@Cacheable은 조회 결과 전체를 메모리에 캐싱해요 ~

10,000개 Movie 객체를 JSON 직렬화하여 저장/읽기하는 건 Redis I/O 비용이 크며,

이걸 네트워크 통해 주고받는 것도 성능 이슈가 될 수 있어요!

즉, 무작정 큰 데이터를 한 번에 캐싱하는 건 오히려 역효과가 발생할수 있어요 !

 

"MSA 아키텍처 자체가 원인이 될 수 있을까요?"

-> 직접적인 원인은 아니라고 생각해요 ~

위 두 부분을 확인한뒤 해당 사항을 고려해보는 것을 추천드려요 !

 

추가로 궁금한 사항 있으시면 언제든 질문 남겨주세요~~ 😄

이수현님의 프로필 이미지
이수현
질문자

먼저 일단 문제는 해결되었습니다! 10000건의 데이터를 100만개의 데이터로 개수를 늘렸고 조회를 100만개를 다 하는것이 아니라 페이징을 적용했더니 큰 차이가 있었습니다. 실제로 Redis 적용전 475ms -> 90ms였는데 Redis 적용 후 600ms->9ms로 큰 성능을 맛볼 수 있었습니다. 감사합니다!

0

안녕하세요, 인프런 AI 인턴입니다. 이수현님께서 Redis 적용 후 성능 저하와 관련하여 질문을 주셨네요. 캐시 적용 후 성능이 개선되지 않는 원인은 여러 가지가 있을 수 있습니다. 몇 가지 가능한 원인과 해결 방안을 제안드리겠습니다.

1. 캐시 미스(Cache Miss)

Redis에 캐시된 데이터가 없는 경우, 캐시 미스로 인해 DB에 다시 접근하여 데이터를 가져오는 데 시간이 소비됩니다. 처음 한 번은 캐시 미스가 발생할 수 있지만, 그 이후로는 캐시에 데이터가 저장되어야 합니다. 실제로 후속 요청에서도 성능 문제가 유지되는지 확인해보세요.

2. Redis 설정 확인

  • Redis의 메모리 사용량이나 저장 가능 용량이 부족하다면, 캐시된 데이터를 유지하지 못하고 만료될 수도 있습니다.
  • Redis 설정 파일이나 메모리 정책을 확인하고 필요한 만큼 자원을 할당했는지 확인하세요.

3. 네트워크 딜레이

애플리케이션 서버와 Redis 서버 간에 네트워크 딜레이가 있으면 성능 저하를 유발할 수 있습니다. 같은 네트워크 환경에서 테스트하거나 네트워크 속도를 측정해볼 필요가 있습니다.

4. 객체 직렬화/역직렬화

  • GenericJackson2JsonRedisSerializer를 사용하셨는데, 데이터 변환 과정에서 성능에 영향을 미칠 수 있습니다. Jackson2JsonRedisSerializer로 변경해서 테스트를 시도해보세요.
  • 대량의 데이터 변환이 필요한 경우, 객체 직렬화에 따른 오버헤드가 발생할 수 있습니다.

5. 방대한 초기 로드

처음부터 모든 데이터를 캐시에 로드하는 방식(findAll())은 대량의 데이터를 불러오면서 성능 저하를 발생시킬 수 있습니다. 페이징을 통해 데이터를 부분적으로 로드하는 방법을 고려해 볼 수 있습니다.

유사 질문 및 추가 자료

아래 유사 질문에서 해결의 실마리를 찾으실 수도 있습니다:
- 캐싱 객체 직렬화/역직렬화

이해에 도움이 되셨기를 바랍니다. 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.

현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

0

이수현님의 프로필 이미지
이수현
질문자

추가적인 Movie와 MovieDto 코드입니다.

@Data
public class MovieDto {

    private Long id;
    private String title;
    private String genre;
    private Integer runningTime;
    private String descreption;
    private String director;

    @JsonFormat(pattern = "yyyy-MM-dd")
    @JsonSerialize(using = LocalDateSerializer.class)
    @JsonDeserialize(using = LocalDateDeserializer.class)
    private LocalDate releaseDate;

}

@Data
@Entity
public class Movie {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String genre;
    private Integer runningTime;
    private String descreption;
    private String director;

    @JsonFormat(pattern = "yyyy-MM-dd")
    @JsonSerialize(using = LocalDateSerializer.class)
    @JsonDeserialize(using = LocalDateDeserializer.class)
    private LocalDate releaseDate;

}
이수현님의 프로필 이미지
이수현

작성한 질문수

질문하기