ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Redis Cache를 통해 이미지 조회 성능 개선하기
    개발/Spring Boot 2024. 5. 24. 23:52

    기존의 이미지 조회 방식에서는 DB에 직접 접근하여 이미지를 가져오는 방식이었다. 우리 서버는 이미지를 조회할 때 항상 Snappy를 통해 압축 해제를 해야 했는데, Snappy의 성능이 아무리 좋아도 항상 일정한 오버헤드를 가진다는 문제가 있었다. 이로 인해 일반 DB 조회보다 더 많은 리소스를 소모하게 되어 성능 저하가 발생했다.

     

    이러한 문제를 해결하기 위해 Image Cache를 구현해야 했다. 캐시를 통해 빈번하게 조회되는 이미지를 메모리에 저장하면, 데이터베이스에 직접 접근하는 빈도를 줄 일 수 있어 조회 성능이 크게 향상될 수 있다. 특히, Redis는 인 메모리 데이터 저장소로 매우 빠른 조회 속도를 제공하여 이미지 조회 성능을 극대화시킬 수 있다.

     

    Redis를 이용하여 Image Cache 기능을 구현하기 위해 Redis Domain을 먼저 정의했다.

    @Getter
    @RedisHash(value = "image")
    public class ImageRedis {
        @Id
        private Long id;
        private final byte[] imageData;
    
        public ImageRedis(Long id, byte[] imageData) {
            this.id = id;
            this.imageData = imageData;
        }
    }
     

    ImageService 클래스에서는 Cache Aside 전략을 택하여 Redis Cache를 활용한다. Cache Aside 전략은 캐시에 데이터가 없는 경우 데이터베이스에서 데이터를 조회하고, 조회한 데이터를 캐시에 저장하는 방식이다.

    @Transactional(readOnly = true)
    public byte[] getImage(Long id) {
        return imageRedisRepository.findById(id)
                .map(ImageRedis::getImageData)
                .orElseGet(() -> {
                    Image foundImage = imageRepository.findById(id).orElseThrow(() -> new CustomNotFoundException("The requested ID was not found."));
                    byte[] decompressedImage = ImageUtils.decompressImage(foundImage.getImageData());
                    ImageRedis imageRedis = new ImageRedis(foundImage.getId(), decompressedImage);
                    imageRedisRepository.save(imageRedis);
                    return decompressedImage;
                });
    }

     

    imageRedisRepository.findById(id) 메서드를 호출하여 Redis 캐시에서 이미지가 있는지 찾아본다. 캐시에서 해당 ID의 이미지 데이터를 찾으면, 이를 반환한다. 이 경우 데이터는 이미 압축 해제된 상태로 저장되어 있으므로, 추가적인 처리가 필요하지 않다. 이 과정을 캐시 히트라고 한다. 캐시 히트가 발생하면 데이터베이스를 조회하지 않아도 되기 때문에 매우 빠르게 응답할 수 있다. 만약 Redis 캐시에 이미지가 없다면, 이는 캐시 미스라고 한다. 이 경우 애플리케이션은 데이터베이스에서 이미지를 조회해야 한다. 우리의 캐시 전략은 Cache Aside이기 때문에 데이터베이스에서 찾아온 데이터를 압축 해제한 후에 Redis Cache에 저장한 후 클라이언트에게 반환한다.

    Redis Cache를 적용한 후 조회 성능 테스트 결과

    Redis를 적용하기 전후의 조회 성능을 테스트하여 얼마나 개선되었는지 확인한다. 테스트는 동일한 환경에서 수행되었으며 다음과 같은 결과를 얻었다.

     

    테스트 환경

    CPU RAM OS 테스트 도구 동작시간 가상유저
    64-bit ARM Cortex-A72 CPU 2GB Ubuntu Server 23.10 (64-bit) nGrinder 5min 99명

     

    평균 TPS

    초기값 (개선 전) 최종 값 (개선 후) 결과
    371.1 523.2 ✅약41% 개선

    결론

    기존의 이미지 조회 방식은 데이터베이스에 직접 접근하여 이미지를 가져오고, Snappy를 통해 압축 해제를 수행하는 방식이었다. 이러한 방식은 Snappy의 성능이 아무리 좋아도 일정한 오버헤드를 수반하게 되며, 결과적으로 일반 DB 조회보다 더 많은 리소스를 소모하며 성능 저하를 초래했다.

     

    이를 해결하기 위해 Image Cache를 도입하여 Redis를 활용한 캐싱 전략을 적용했다. Redis와 같은 인메모리 데이터 저장소를 사용하여 빈번하게 조회되는 이미지를 캐시에 저장함으로써 데이터베이스 접근 빈도를 줄이고, 응답 시간을 대폭 단축할 수 있었다. 특히, Cache Aside 전략을 적용하여 캐싱에 데이터가 없는 경우에만 데이터베이스를 조회하고, 조회한 데이터를 캐시에 저장하는 방식으로 효율성을 극대화했다.

     

    가장 중요한 개선점은 Snappy 압축 해제로 인한 성능 손실을 더 이상 겪지 않는다는 점이다. 이제 이미지는 압축 해제된 상태로 캐시에 저장되며, 캐시에서 직접 데이터를 제공하기 때문에 Snappy 압축 해제 과정에서 발생하는 오버헤드를 피할 수 있게 되었다. 이로써 데이터베이스 부하를 줄이는 것뿐만 아니라, 압축 해제 과정에서 발생하는 추가적인 리소스 소모를 제거하여 전체적인 시스템 성능을 크게 향상했다.

     

    댓글

Designed by Tistory.