개발 기록

> 캐싱에 대해 알아보자! 캐시는 언제 써야 할까? 본문

DIVE

> 캐싱에 대해 알아보자! 캐시는 언제 써야 할까?

1z 2024. 1. 5. 21:22

 

 

1. 캐싱이란?

캐싱(caching) 은 자주 사용되는 데이터나 계산된 결과값을 미리 저장해 두고, 이후 요청 시에 이를 재사용하는 기법이다. 이를 통해 반복적인 데이터 접근이나 계산을 피하고, 시스템의 응답 속도를 향상 시킬 수 있다.

 

※ 캐시의 종류와 그 예시  

① CPU 캐시: L1, L2, L3 캐시

② 운영체제 캐시: 페이지 캐시, 디스크 캐시

데이터베이스 캐시: MemCached, Redis

ex. 자주 조회되는 상품 목록을 캐시에 저장하고, 쿼리 최적화와 DBMS 성능 튜닝을 통해 캐시 적중률을 높인다.

④ API 캐시

⑤ HTTP 캐시: CDN, 브라우저 캐시

ex. CDN: CDN 서버는 자주 요청되는 웹 콘텐츠( CSS, JavaScript 파일 등)를 캐시하여 사용자에게 가까운 서버에서 제공

ex. 브라우저 캐시: 이미지가 브라우저 캐시에 저장되어 재방문 시 빠르게 로드

⑥ DNS 캐시: 사용자가 자주 방문하는 웹사이트의 도메인 이름을 캐시하여 재방문 시 빠르게 IP 주소를 해석

⑦ 마이크로서비스 캐시

 


 

캐싱은 데이터 접근 속도를 크게 향상시킬 수 있는 유용한 도구이지만, 모든 상황에서 적합하지는 않는다. 캐싱하기 적합한 데이터를 선택하고, 실시간성이 요구되는 데이터는 캐싱하지 않는 것이 중요하다. 시스템 특성에 맞게 캐시 전략을 설계 해야한다!!

2. 캐싱은 언제 사용하는게 좋을까?

(1) 캐싱하기 적합한 데이터

반복적이고 동일한 결과가 나오는 기능의 결과값 (ex. 자주 조회되는 사용자 프로필 정보, 인기 상품 목록 등)

자주 조회되면서 업데이트가 자주 발생하지 않는 데이터 (ex. 날짜별 날씨 정보, 뉴스 기사 등)

입력값과 출력값이 일정한 데이터 (ex. 특정 수학적 계산 결과, 고정된 데이터 조회)

작업 시간이 오래 걸리거나 서버에 부담을 주는 작업 (ex. 외부 API 호출 결과, 복잡한 데이터베이스 쿼리 결과 등)

Hit 확률이 높을 경우 (ex. 특정 키 값이 자주 호출되는 데이터, 예를 들어 인기 검색어 순위 등)

 

(2) 캐싱하기 적합하지 않은 경우

실시간으로 변화하고 민감한 데이터 (ex. 결제 상태, 쿠폰 적용, 주식 거래 데이터 등)

캐시 서버가 따로 없을 경우 캐싱을 자주 하는 것이 오히려 애플리케이션 서버에 부담을 줘서 서버 다운이나 서비스 중단을 일으킬 수 있다.

데이터가 자주 변경되거나 실시간성이 중요한 경우, 긴 캐싱 시간은 데이터의 신뢰성을 떨어뜨릴 수 있다.

 


 

3. 캐시 사용 시 고려해야 할 주요 사항

Cache TTL (Time To Live, 캐시에 저장된 데이터의 유효기간

- 각 캐시 항목에 만료일자를 설정하여 일정 기간 동안만 데이터를 유지하고, 기간이 지나면 삭제되도록 해야한다. 또한 TTL이 만료된 데이터는 다시 요청될 때 재캐싱되도록 한다. 이를 통해 데이터가 지속적으로 최신 상태를 유지할 수 있다.

 

데이터를 무기한 저장하는 것은 메모리 자원 낭비 이다. 특히 미사용 데이터나 유효하지 않은 데이터가 계속해서 메모리를 차지하면, 중요한 데이터가 캐시에 저장되지 못할 수 있다.

 

메모리 관리

- 캐시는 메모리 내에 데이터를 저장하기 때문에, 메모리 관리가 중요하다.

- 캐시에 데이터가 메모리에 가득차게 되면, 정의한 삭제 알고리즘(ex. LRU, LFU, FIFO)에 따라 데이터를 삭제하여 새로운 데이터를 저장할 공간을 만든다.

- Java GC(Garbage Colletion)가 동작할 때 캐시된 데이터가 너무 많이 쌓이면 GC가 메모리 해제를 제대로 수행하지 못해 제거 대상이 되는 캐시 데이터를 제거하지 못한다. 이는 즉 메모리 부족상황을 초래하여 OOM(Out Of Memory Error) 오류가 발생할 수 있다. 그러므로 캐시가 사용하는 메모리와 GC가 해제할 수 있는 여유 메모리를 충분히 고려하여, 캐시가 메모리를 효율적으로 사용하고, GC가 메모리를 효과적으로 관리하도록 설계 해야한다.

 

▼ 캐시 삭제 전략 (아래)

더보기

※ 참고(삭제 알고리즘)

LRU (Least Recently Used): 가장 오랫동안 사용되지 않은 데이터를 삭제

LFU (Least Frequently Used): 사용 빈도가 가장 낮은 데이터를 삭제

FIFO (First In, First Out): 가장 먼저 들어온 데이터를 가장 먼저 삭제

캐시와 메모리 간의 데이터 관리방법  (아래)

더보기

1. Look-Aside (Lazy Loading): 데이터를 요청 했을 때 데이터를 캐시에 로드하는 캐싱 전략

(캐시에서 우선 조회 -> 조회 실패시 : DB에서 조회 후 그 결과를 캐시한 후에 데이터를 내보냄)

 

장점: 캐시의 적중률을 높일 수 있으며, 시스템 전체적인 성능을 향상시킬 수 있다.

단점: 초기에는 캐시에 데이터가 없을 수 있고, 이로 인해 일시적인 지연이 발생할 수 있다.

 

2. Write-Back: 캐시 메모리에서의 데이터 수정을 메인 메모리에 즉시 반영하지 않고, 캐시 내부에 변경된 데이터를 유지하는 방식

(ex. 데이터를 캐싱해뒀다가 특정 시점마다 한번씩 캐시 내 데이터를 DB에 insert 및 update 하는 방법)

 

장점: 쓰기 연산의 성능을 향상시킬 수 있다. 

단점: 데이터를 일정 기간 유지하고 있는 동안 서버 장애 상황에서 데이터가 손실될 수 있다.  또한 메모리와 캐시 간의 데이터 일관성을 관리하기 위한 별도의 제어 로직이 필요하다.

 

실제 데이터와 캐시 데이터와의 정합성 

- 캐시된 데이터가 실제 데이터와 일치하지 않을 때 발생하는 문제

(ex. 데이터베이스에서 특정 링크를 삭제했지만 캐시에는 여전히 저장되어 있다면, 사용자가 이 링크에 접근할 때 HTTP 404 오류 발생)

- 해결방법: 캐시 무효화, Write-through 또는 Write-behind(변경 사항 반영시 캐시 동시 업데이트) 

 

인풋과 아웃풋의 의존 관계

- 요청에 필요한 모든 데이터가 캐시에 있어야 한다. 일부 데이터만 캐시에 있고 나머지는 없으면, 불완전한 요청 처리, 성능저하, 일관성 문제가 발생한다. 

- 해결 방법: 요청에 필요한 전체 데이터 캐싱, 업데이트 관리, 프리로드(preload)


4. 캐시 히트율과 캐싱 전략

캐시 히트 확률은 캐시에 저장된 데이터에 대한 요청이 얼마나 자주 성공적으로 이루어지는지를 나타내는 비율을 말한다. 쉽게 말해, 캐시 히트 확률은 캐시에서 데이터를 찾을 수 있는 빈도를 측정하는 지표이다.

 

 

 

  • 캐시 히트(Cache Hit): 요청된 데이터가 캐시에 이미 저장되어 있어, 캐시에서 직접 데이터를 반환할 수 있는 경우.
  • 캐시 미스(Cache Miss): 요청된 데이터가 캐시에 존재하지 않아, 원래 데이터 소스(DB, API 등)에서 데이터를 가져와야 하는 경우.

(1) 캐시 히트 확률을 높이는 방법

  1. 적절한 데이터 선택: 자주 접근하는 데이터나 변경 빈도가 낮은 데이터를 캐싱한다.
  2. 적절한 캐시 크기 설정: 충분한 메모리를 할당하여 자주 사용되는 데이터가 캐시에 남아 있도록 한다.
    1. 너무 작은 캐시는 캐시 미스가 자주 발생하게 하지만, 너무 큰 캐시는 적중률을 높이지만 비용 문제가 발생할 수 있다.
  3. 효과적인 캐시 전략: LRU(Least Recently Used), LFU(Least Frequently Used) 등의 전략을 사용하여 불필요한 데이터를 제거하고 중요한 데이터를 유지한다.
  4. 캐시 최적화 기법 사용: 동적 캐시 크기 조정, 다중 레벨 캐시 등
  5. 적절한 캐시 매핑 방식 선택: 캐시 충돌을 최소화하고 캐시 히트를 증가시킨다
    1. 세트 연관 방식은 캐시 충돌 가능성이 줄어들어 적중률을 높일 수 있고, 직접 매핑은 간단하고 저렴하지만 같은 세트에 다수의 데이터가 매핑될 경우 충돌 가능성이 높고 캐시 미스가 발생할 수 있다.

* 참고

- Set Associative (세트 연관): 캐시를 여러 개의 세트로 나누고, 각 세트는 몇 개의 블록을 저장할 수 있다.

- Direct Mapping (직접 매핑): 메모리의 주소를 특정 세트에 직접 매핑하여 그 세트 안에서 태그를 사용하여 데이터 블록을 식별하는 방식

 


 

캐시를 언제, 어떻게 사용하면 좋을 지 알아봤다. 그렇다면 어떤 캐시 저장소를 선택해야 할까?

5. 로컬 캐시 vs 글로벌 캐시 vs 분산 캐시

  Local Cache Global Cache
저장소 개별 시스템 또는 서비스 내에 존재하는 캐시 메모리 여러 시스템이 공유하는 중앙화된 캐시 메모리
목적 주로 개별 사용자 또는 프로세스의 성능 향상을 목적으로 함 여러 시스템이 공통으로 접근 가능하며, 전역적인 데이터 공유를 목적으로 함
장점 - 데이터 접근 속도가 빠름
- 각 시스템에서 데이터 독립적으로 관리 가능
- 여러 시스템 간 데이터 공유 용이
- 데이터 일관성 유지 가능
단점 - 다른 시스템과의 데이터 공유 어려움
- 시스템 간 데이터 불일치 문제 발생 가능
- 네트워크 트래픽을 사용해야 해서 로컬 캐시보다 느림
- 전역 캐시의 관리와 동기화가 복잡함
ex  Ehcache, Caffeine 등 Redis

 

※  보통 다른 서비스에 전혀 영향을 받지 않는 데이터이고 캐싱이 필요하다면 Local Cache를 사용하고 전체적인 서비스가 공유되어야 되는 데이터 이지만 캐싱이 필요하다고 하면 Global Cache를 사용한다.

 

  분산 캐시
저장소 여러 노드에 분산된 캐시 메모리
목적 캐시 데이터를 여러 노드에 분산하여 확장성을 높이고 성능을 개선하는 것을 목적으로 함(=부하 분산)
특징 - 데이터가 여러 노드에 분산되어 저장되며, 보통 각 노드는 일부 데이터의 캐시 복제본을 가지고 있음
- 캐시 데이터는 일관된 해싱에 의해서 각 분산 캐시 노드별로 분할되어 저장된다. 이 경우 각 노드는 작은 캐시 데이터을 가지고 있으며, 그런 다음 원본으로 이동하기 전에 다른 노드에 데이터 요청을 전송한다.
장점 - 높은 확장성과 성능 (요청 풀에 노드를 추가하는 것만으로 캐시 공간을 쉽게 늘릴 수 있음)
- 부하 분산이 가능하며 단일 장애점이 없
단점 - 노드 증가에 따른 네트워크 오버헤드와 복잡성 증가 가능성
- 데이터 일관성 유지 어려움
(두 노드에서 동시에 동일한 캐시의 동일한 데이터에 대한 변경을 수행할 경우, 두 노드 사이에 데이터 불일치가 발생할수 있음)

- 누락된 캐시 노드 
ex Memcached, Redis

 

 

※ 분산 캐시와 글로벌 캐시는 비슷한 개념이지만 약간의 차이가 있다. 글로벌 캐시는 모든 노드가 동일한 단일 캐시 공간을 사용하고  분산 캐시의 각 노드는 캐시된 데이터의 일부를 저장하고 있다. 

 

 

 

 

참고

https://velog.io/@zenon8485/%EC%BA%90%EC%8B%B1%EA%B3%BC-%EC%BA%90%EC%8B%9C-%EB%AC%B4%ED%9A%A8%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-%EA%B8%80

https://jeong-pro.tistory.com/170

https://yozm.wishket.com/magazine/detail/2296/