캐시 도입한 후 응답 속도 47% 향상?

지송·2024년 11월 24일

방끗

목록 보기
7/9

안녕하세요!
오늘은 방끗 프로젝트에 캐싱을 도입한 얘기를 하려고 합니다

저희는 기존에 ENUM으로 관리하던 카테고리와 질문 데이터를 데이터베이스로 옮기는 작업을 진행했습니다
이 작업은 카테고리와 질문이 기획에 따라 자주 변경될 가능성이 있는 정보이므로, 기획 변경 시마다 해당 ENUM을 수정하여 프로젝트를 재배포하는 방식은 올바른 설계가 아니다라는 판단에서 이루어진 결정이었습니다

그러나, 모든 유저가 질문과 카테고리를 조회할 때마다 데이터베이스에 접근하는 방식은 성능 면에서 비효율적이라고 느꼈습니다
이 부분을 개선하면 사용자들에게 더 빠르고 효율적으로 정보를 제공할 수 있지 않을까 하는 고민이 생겼습니다

따라서 사용자마다 변치 않는 정보인 전체 질문 전체 카테고리 아티클을 캐싱하기로 결정하였어요

로컬 캐시 vs 글로벌 캐시

그렇다면 캐시를 도입할 전략을 택해야 해요
로컬 캐시와 글로벌 캐시 중 무엇을 도입하면 좋을까요?
특징을 비교해 봅시다

특징로컬 캐시글로벌 캐시
속도매우 빠름상대적으로 느림 (네트워크 비용)
일관성서버별로 다를 수 있음모든 서버가 동일한 데이터 참조
설정 및 운영간단함복잡함 (클러스터 관리 등)
확장성서버 증가 시 동기화 어려움서버 증가와 무관
데이터 크기제한적 (서버 메모리 의존)대용량 처리 가능

저희는 위 사안들을 고려했을 때 로컬 캐시를 도입하기로 했어요

방끗 서버는 2대의 서버를 로드 밸런서로 조율하기 때문에 글로벌 캐시를 사용해야 하는 것 아닌가? 생각이 들 수도 있지만
저희가 캐시를 사용하는 목적은 DB에 있는 데이터를 그대로 캐시에 올려 사용하는 것이기 때문에 데이터 문제가 발생하지 않아요

캐시 전략

다만, 저희가 DB의 데이터를 변경할 경우에 캐시에 반영이 되지는 않는다는 단점이 있죠
해당 데이터들은 critical한 데이터가 아니라고 판단하여 하루마다 캐시 만료를 통해 길어도 하루가 지나면 새로운 데이터가 반영되도록 설계하였어요

사실 기획적으로도 생각해 보자면 사용자가 과거에 작성한 체크리스트를 조회했을 때 기존 질문과 바뀌어 있다면 혼돈이 있을 거라 판단하여 질문을 수정, 삭제하는 것은 배제하고 최신 질문 태그를 두어 관리하려고 했어요

이러한 고려 끝에 캐시는 하루가 지나면 만료시키는 전략을 수립하였습니다

ehcache vs caffeine cache

로컬 캐시 중에서도 저희가 선택할 수 있는 캐시는 꽤 여러 개 있어요
저희는 그중에서도 ehcache와 caffeine cache 둘 중 하나를 선택하고자 결심했는데요

기술이나 도구를 도입할 때 가장 중요한 요소 중 하나는 커뮤니티가 잘 형성되어 있나? 라고 생각해요
문제가 발생했을 때 도움을 받을 수 있는 다양한 자료가 많으며,
많은 유저들이 사용하는 만큼 경쟁력이 있음은 빼놓을 수 없는 요소라고 생각합니다

검색 결과 두 캐시의 자료가 가장 많았기에 두 개를 두고 고민하기 시작했습니다

니들이 caffeine 맛을 알아?
로컬 캐시 선택하기

해당 글들을 참고한 결과 단순하고 성능이 좋은 caffeine으로 결정하여 진행하기로 했습니다!
위 블로그에 두 개의 성능과 기타 사항들을 비교한 것이 잘 기록되어 있어서 참고하시면 좋을 듯해요 👍

카페인을 도입해 보자

캐시를 도입하는 것은 사실 엄청나게 쉬운데요

  1. config 작성하기

  2. 캐시 적용하기

  3. 캐시 이름 관리하기
    cacheNames에 일일히 String을 적는 것보다는 관리 변수를 하나 두어 일괄적으로 적용하는 것이 더 좋은 설계라고 생각하여 이름을 관리하는 클래스에 public static 변수로 두었어요

    원래는 ENUM으로 관리하고 싶었는데 어노테이션 안에서는 name() 함수 적용이 안 되어서 아래 방향으로 구현하였습니다

  1. 캐시 테스트 해 보기
    그 다음 두 번 조회 시 한 번만 repository 호출이 된 것 또한 테스트하였습니다

이렇게 캐시를 도입하였습니다

응답 속도 측정

캐시를 도입하고 나서 얼마나 유의미한 개선이 있었는지 파악하고 싶었어요
따라서 k6를 통해서 부하 테스트를 진행하였습니다

측정 조건

  • 동시 접속자 수 20명
  • 60초 동안 요청

측정할 API들

  • @GetMapping("/checklists/questions") → 체크리스트 질문 7.59% 향상
    캐싱 전에는 95%의 사용자가 응답을 받는 데 165.67ms가 걸렸으나

    캐싱 후에는 153.09ms 소요되었습니다
  • @GetMapping("/custom-checklist/all") → 초반에 커스텀 체크리스트 모두 가져올 때 39.08% 향상
    캐싱 전에는 95%의 사용자가 응답을 받는 데 423.52ms가 걸렸으나

    캐싱 후에는 257.99ms 소요되었습니다

  • @GetMapping("/articles") 47.22% 향상
    캐싱 전에는 95%의 사용자가 응답을 받는 데 416.04ms가 걸렸으나

    캐싱 후에는 219.58ms 소요되었습니다

마무리

전략 수립부터 캐시 도입, 응답 속도 측정까지 일련의 과정을 담아보았는데요
유의미한 응답 속도 개선으로 이어져 뿌듯한 시간이었습니다!

글 읽어 주셔서 감사합니다 🙇‍♂️

profile
💻 늘 공부하고 발전하는 개발자

0개의 댓글