Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

AI가 번역한 다른 언어 보기

제이온

[DB] 캐시를 설정하는 기준

  • 작성 언어: 한국어
  • 기준국가: 모든 국가 country-flag

언어 선택

  • 한국어
  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

durumis AI가 요약한 글

  • 캐시는 자주 읽히지만 쓰기 빈도가 낮은 데이터에 적용하는 것이 효율적이며, APM을 활용하여 RDB 쿼리 호출 내역을 분석하여 캐싱 대상 데이터를 선정합니다.
  • 최신화에 민감한 데이터는 캐싱 적용 시 주의가 필요하며, 로컬 캐싱은 속도는 빠르지만 인스턴스 간 캐시 동기화 문제가 발생할 수 있어, 글로벌 캐싱은 속도는 느리지만 데이터 동기화 및 관리가 용이합니다.
  • 저는 여러 인스턴스 환경에서 4MB 이하의 결제 관련 데이터에 대해 로컬 캐싱을 적용하고, 최신화 중요도를 고려하여 5초 TTL을 설정하였으며, 향후 성능 테스트를 통해 효율성을 검증할 예정입니다.

안녕하세요? 제이온입니다.

오늘은 캐시를 설정하는 기준을 설명드리려 합니다. 개인적으로 실무를 겪으면서 작성하는 포스팅이므로, 어디까지나 참고로 봐주시면 좋을 것 같습니다 ㅎㅎ


Cache란?

캐시는 나중에 요청할 결과를 미리 저장해 둔 후 빠르게 서비스해 주는 것을 의미합니다. 즉, 미리 결과를 저장하고 나중에 요청이 오면 그 요청에 대해서 DB 또는 API를 참조하지 않고 캐시에 접근하여 요청을 처리하는 기법입니다. 이러한 캐시가 나온 배경에는 파레토 법칙이 있습니다.

파레토 법칙은 80%의 결과는 20%의 원인으로 인해 발생한다는 뜻으로, 아래 사진을 참고해 주시면 좋을 것 같습니다!



즉, 캐시는 모든 결과를 캐싱할 필요가 없으며 서비스를 할 때 많이 사용되는 20%만 캐싱함으로써 전체적으로 효율을 끌어올릴 수 있습니다.


어떤 데이터를 캐싱해야 할까?

파레토 법칙에 의해 아무 데이터를 캐싱하면 안 되고, 꼭 필요한 데이터만 캐싱해야 합니다. 그렇다면, 어떠한 데이터를 캐싱해야 할까요?


자주 읽어야 하지만 쓰기가 거의 일어나지 않는 데이터

바로, “자주 읽어야 하지만 쓰기가 거의 일어나지 않는 데이터에 대해서 캐싱해야 한다.” 라고 이론적으로 많이 이야기하는데 “자주 읽음”의 기준과 “쓰기가 거의 일어나지 않음”의 기준이 상당히 모호했습니다.


그래서 저는 다음과 같은 스텝으로 캐싱할 데이터를 조사합니다.


  • 데이터 독과 같은 APM을 통해 RDB의 쿼리 호출 내역 TOP 5를 확인합니다.
  • 그 중, 조회 쿼리를 찾고 어떤 테이블로부터 조회를 해 오는지 확인합니다.
  • 해당 테이블의 업데이트 쿼리가 얼마나 불리는지 확인합니다.


위와 같은 프로세스를 거치면서 조회가 많지만 업데이트 쿼리가 적게 발생하는지 보는 것입니다. 제가 실무에서 확인한 테이블은 조회 쿼리가 하루에 174만 번 발생했는데, 업데이트 쿼리는 많아야 500번 정도 발생했습니다. 이정도면 누가봐도 캐시에 적합한 조건이라 봐도 되겠습니다 ㅎㅎ


최신화에 민감한 데이터

최신화에 민감한 데이터는 RDB와 캐시 사이에 불일치가 짧아야 한다는 뜻입니다. 가령, 결제와 관련된 정보의 경우 최신화에 매우 민감하기 때문에 위의 캐시 조건에 맞더라도 적용을 고민해 봐야 합니다.


저는 위의 2가지 특성에 맞는 결제 관련 테이블에 대해 캐싱을 해야 했습니다. 그래서 해당 결제 관련 테이블을 사용하는 모든 로직에서 캐시를 적용하지는 않았고, 실질적으로 결제가 일어나지 않는 비교적 안전한 로직에 부분적으로 캐싱하기로 결정했습니다.


로컬 캐싱 vs 글로벌 캐싱

이제, 캐싱할 데이터와 캐싱할 범위는 어느 정도 정했습니다. 그렇다면, “어디”에 캐싱 데이터를 저장할지 고민해야 합니다. 일반적으로 로컬 메모리에 저장하거나, Redis와 같은 별도 서버에 저장할 수 있습니다.


로컬 캐싱

로컬 캐싱은 애플리케이션 서버의 메모리에 캐싱할 데이터를 저장하는 방법이며, 일반적으로 Guava cache나 Caffeine cache를 많이 사용합니다.


장점

  • 애플리케이션 로직을 수행하다 바로 같은 서버 내의 메모리에서 캐시를 조회하므로 속도가 빠릅니다.
  • 구현하기 쉽습니다.


단점

  • 인스턴스가 여러 개일 경우 여러 문제점이 생깁니다.
    • 한 인스턴스에서 변경한 캐시를 다른 인스턴스에 전파할 수 없습니다. 단, 변경을 다른 인스턴스에 전파하는 로컬 캐싱 라이브러리도 있습니다.
    • 각 인스턴스마다 캐시가 저장되므로 새로운 인스턴스가 뜨면 캐시를 새로 넣어야 합니다. 이로 인해 캐시 미스가 많이 발생하여 트래픽을 견디지 못해서 인스턴스가 죽을 수 있습니다.


글로벌 캐싱

글로벌 캐싱은 Redis와 같은 별도로 캐시 데이터를 저장하는 서버를 두는 방식입니다.


장점

  • 인스턴스간 캐시를 공유하므로 한 인스턴스에서 캐시를 수정하더라도 모든 인스턴스가 동일한 캐시 값을 얻을 수 있습니다.
  • 새로운 인스턴스가 뜨더라도 이미 있는 캐시 저장소를 바라보면 되므로, 캐시를 채워 넣는 동작을 할 필요가 없습니다.


단점

  • 네트워크 트래픽을 거쳐야 하므로 속도가 로컬 캐싱에 비해 느립니다.
  • 별도의 캐시 서버를 두어야 하므로 인프라 관리 비용이 생깁니다.
    • 인프라 관리 비용? → 서버 요금, 인프라 세팅 및 유지 보수하는 데 걸리는 시간, 장애 대응 대책 구상 등


필자는 어떤 것을 선택했는가?

현재 회사의 애플리케이션 서버는 여러 인스턴스를 띄우는 구조이지만, 로컬 캐시를 선택했습니다.

크게 이유는 3가지가 있습니다.


  • RDB에 저장된 캐싱할 데이터가 4만개가 좀 되지 않아, 이를 모두 메모리에 올려도 4MB 이하입니다.
  • 결제 관련된 데이터에 대해 조회 성능이 좋아야 했습니다.
  • Redis가 기존에 있기는 하나, 새로운 캐시를 Redis에 저장하는 것이 인프라 비용이 생깁니다.


캐시를 어떻게 최신화할 것인가?

애플리케이션 서버가 여러개 있고, 여기에 로컬 캐싱을 적용하였다면 각 애플리케이션 서버 별로 저장된 캐시 값이 다를 수 있습니다. 가령 A 서버의 저장된 캐시 데이터는 “1”이지만, B 서버의 저장된 캐시 데이터는 B 서버에서 변경하여 “2”가 될 수 있습니다. 이 상황에서 사용자가 로드밸런서에 요청을 보내면 A 서버와 B 서버에서 상이한 값을 받게 됩니다.


따라서 각 인스턴스 별로 캐시를 자동으로 제거하여 RDB로부터 조회하도록 구성해야 하는데, 이때 TTL을 주로 사용합니다.


TTL은 얼마로 설정해야 하는가?

TTL은 Time To Live의 약어로, 특정 시간이 지나면 캐시를 지우는 설정입니다. 가령, TTL을 5초로 설정하였다면 캐시 데이터는 5초 뒤에 자동으로 지워집니다. 그 후, 캐시 미스가 발생하면 RDB로부터 데이터를 조회해 와서 저장합니다.

그렇다면, TTL은 얼마로 설정해야 할까요?


read/write가 하나의 캐시 서버에서 발생

read/write가 Redis와 같은 글로벌 캐싱 서버 하나에서 발생하거나, 로컬 캐싱이 적용된 하나의 애플리케이션 서버에서 발생한다면, TTL의 값은 시간 단위 이상으로 올려도 무방합니다. 어차피 write할 때 기존 캐시를 수정할 것이고, 해당 캐시에서 데이터를 가져오는 서버는 항상 최신화된 데이터를 보게 됩니다.


이 경우 TTL을 설정하지 않고, 캐시 서버가 꽉차면 LRU 알고리즘을 통해 자동으로 캐시를 조금씩 비우는 방식으로 구성할 수도 있습니다.


read/write가 여러 캐시 서버에서 발생

read/write가 다중화된 글로벌 캐싱 서버에서 발생하거나, 로컬 캐싱이 적용된 여러 개의 애플리케이션 서버에서 발생한다면, TTL은 초 ~ 분 단위로 가져가는 것이 좋습니다. 왜냐하면 수정된 데이터를 아직 반영하지 않는 캐시 서버의 오래된 데이터를 읽을 가능성이 있기 때문입니다.


이때 TTL은 다양한 맥락에서 결정되는데, 최신화가 중요하고 값이 변경될 확률이 높을수록 TTL을 짧게 가져 가야 하고, 최신화가 덜 중요하고 값이 변경될 확률이 낮을수록 TTL을 조금 더 길게 가져가도 됩니다.


필자는 TTL을 어떻게 설정하였는가?

제가 캐싱할 데이터는 결제와 관련한 데이터고, 실질적으로 결제가 발생하는 엄밀한 로직에는 캐싱을 적용하지 않더라도 어쨌든 결제 특성상 최신화가 중요합니다. 다만, 업데이트 가능성은 낮기 때문에 TTL은 5초 정도로 안전하게 가져갔습니다.


결론

정리해 보면, 제가 선택한 캐싱 방식을 다음과 같습니다.


  • 결제 관련 데이터
  • 조회가 매우 빈번히 일어나지만, 수정은 거의 일어나지 않음.
  • 실질적으로 결제가 일어나지 않지만, 조회가 발생하는 로직에 한해 캐싱을 적용함.
  • 로컬 캐싱을 적용하였으며, TTL은 5초로 설정함.


앞으로 할 일은 실제로 적용한 캐싱 방식에 한해 성능 테스트를 진행하는 것입니다. 아직 구체적으로 어떻게 성능 테스트를 진행할지는 고민 중이어서 이후 포스팅에서 작성해보겠습니다!

제이온
제이온
제이온
제이온
[이펙티브 자바] 아이템 6. 불필요한 객체 생성을 피하라 자바에서 불필요한 객체 생성을 최소화하여 성능을 향상시키는 방법에 대해 설명합니다. 문자열, 부울, 정규 표현식, 뷰 객체, 오토 박싱 등 다양한 예시와 함께 객체 재사용의 중요성을 강조합니다. 특히 방어적 복사가 필요한 경우 객체 재사용으로 인한 문제점을 주의해야 합니다.

2024년 4월 28일

[Spring] @Async 사용 방법 Spring @Async를 사용하여 Java에서 비동기 처리를 구현하는 방법을 설명합니다. @EnableAsync 어노테이션을 사용하여 비동기 메소드를 선언하고, ThreadPoolTaskExecutor를 사용하여 스레드 풀을 구성하여 효율적인 비동기 작업 처리를 수행할 수 있습니다. Future, ListenableFuture, CompletableFuture 등 다양한 리턴 타입을 사용하여 비동기 메소드의 결과를 처리하는 방법도 다룹니다.

2024년 4월 25일

[오브젝트] 2장. 객체 지향 프로그래밍 영화 예매 시스템을 객체 지향 프로그래밍으로 구현하는 방법을 설명합니다. 영화, 상영, 사람, 할인 정책, 할인 조건 등의 객체를 정의하고, 객체 간의 협력을 통해 할인 요금을 계산하는 방법을 제시합니다.

2024년 4월 28일

[비전공, 개발자로 살아남기] 14. 신입 개발자 자주 묻는 기술면접 내용 요약 신입 개발자 면접에서 자주 나오는 기술 질문과 답변을 정리했습니다. 메모리 영역, 자료구조, 데이터베이스, 프로그래밍 패러다임, 페이지 교체 알고리즘, 프로세스와 스레드, OSI 7 계층, TCP와 UDP 등 다양한 주제를 다룹니다.
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

2024년 4월 3일

#마케팅 - 마케터의 캘린더는 빈 공간이 없다. 프로모션 기획 시 시간 낭비를 줄이기 위한 두 가지 방법을 제시합니다. 빠른 의사 결정을 위해 외부 테스트를 통해 데이터를 확보하고, 위험 관리를 위해 마케팅 캘린더를 활용하여 일정을 체계적으로 관리하는 것이 중요합니다.
30대의 존버살이를 씁니다.
30대의 존버살이를 씁니다.
30대의 존버살이를 씁니다.
30대의 존버살이를 씁니다.
30대의 존버살이를 씁니다.

2024년 1월 17일

슬롯 필링(Slot-Filling)이란? 챗봇이 사용자로부터 필요한 정보를 모두 얻을 때까지 계속 질문하는 방식을 슬롯 필링이라고 합니다. 예를 들어, 챗봇이 커피 주문을 받을 때, 커피 종류, 온도, 잔수를 모두 입력받아야 주문이 완료됩니다.
꿈많은청년들
꿈많은청년들
슬롯 필링이라고 크게 쓰인 이미지
꿈많은청년들
꿈많은청년들

2024년 5월 13일

Redis 7.4 - 라이선스 정책 변화 Redis는 메모리를 사용하는 인메모리 데이터베이스로, 빠른 속도와 간편한 키-값 구조가 장점입니다. 최근 Redis는 라이선스 정책을 변경하여 클라우드 서비스 제공업체는 Redis와의 라이선스 계약을 체결해야만 Redis 7.4 이상 버전을 제공할 수 있게 되었습니다. 일반 개발자에게는 큰 변화가 없으며, Redis Community Edition을 통해 무료로 사용할 수 있습니다.
해리슨 블로그
해리슨 블로그
해리슨 블로그
해리슨 블로그

2024년 3월 21일

투자를 확률론적 사고방식으로 접근해야 하는 이유: 투자결과의 정확한 원인을 평생 알 수 없다 투자 결과는 운에 의해 좌우되는 경우가 많으며, 우리는 그 원인을 정확히 알 수 없다는 것을 인지해야 합니다. 따라서 투자는 확률론적 사고방식으로 접근하여 유리한 곳에서는 장기간 투자하고, 불리한 곳에서는 빨리 손절하는 것이 중요합니다.
고집스런가치투자
고집스런가치투자
고집스런가치투자
고집스런가치투자

2024년 4월 3일

투자분석능력을 향상시켜 주는, 작지만 중요한 습관 3가지 타인이 만든 자료를 그대로 받아들이지 말고, 자료의 근거를 직접 확인하고 분석하여 실수를 잡아내고 벤치마킹하는 습관을 들여야 합니다.
고집스런가치투자
고집스런가치투자
고집스런가치투자
고집스런가치투자

2024년 4월 3일