캐시(Cache)와 Cache전략

TAS
7 min readJul 30, 2022

--

웹 서비스의 성능을 높이는 방법 중 하나로 cache를 사용하는 방법을 사용한다. 캐시에 대한 내용을 정리하고 캐시 설계 전략에 대해서 정리하고자 한다. 그리고 Redis를 사용하여 Cache 사용을 실제로 구현해 본다.

** 키워드 **
캐시의 개념 ( 사용 목적 )
Local/Global Cache
설계 고려사항 -> 4가지
Expired Time - TTL 설정
분산 Lock

캐시란? ( Cache란? )

자주 사용하는 데이터나 값을 미리 복사해 놓은 임시 장소를 가리키는 용어이다. ( 위키 백과 ) 캐시는 보통 A > B 일 때 시간을 줄이고자 사용하는 경우가 많다.

  • A : 주 메모리에 접근하는 시간
  • B : 캐시 메모리에 접근하는 시간

또한 반복적으로 동일한 결과를 돌려주는 데이터가 많을 경우에도 자주 사용한다.

캐시의 구분

캐시는 크게 Local cache와 Global cache가 있다.

[ Local cache ]

  • Local 환경에서만 사용하는 캐시. 예를 들면 MSA에서 특정 service에서 사용하는 로컬 캐시는 해당 서비스에서만 사용.
  • local에서만 동작하기 때문에 해당 캐시 프로세스를 사용하는 프로세스는 데이터 접근 속도가 매우 빠르다.
  • 반면 별도의 구분 된 환경에서는 해당 local 캐시에 접근이 불가능하기 때문에 별도의 프로세스를 통해서 데이터 공유가 가능함.

[ Global cache ]

  • 특정 별도의 구분 된 환경에 캐시를 띄우고 다른 환경에서 해당 환경으로 접속하여 사용하는 캐시이다.
  • 구분 된 별도의 환경 간 네트워크 통신이 필요하기 때문에 local cache에 비해서는 느리다.

[ 선정 기준 ]

  • 캐시에 저장되는 데이터의 종류와 그 데이터가 일관성이 깨졌을 경우, 비즈니스에 주는 영향을 고려해서 선정한다.
  • 대체로 최근 트렌드는 비즈니스에 따른 아키텍처의 영향을 받기 때문에, 아키텍처를 고려해야 함.
  • MSA 관점에서 보았을 때 여러개의 서비스에서 캐시에 접근하는 경우나, 각 서비스별 캐시의 데이터 일관성이 중요한 경우라고 한다면 글로벌 캐시를 사용하는 것이 유리하다고 판단 할 수 있음.

캐시 설계 고려사항

사용하는 환경에 따라서 조금씩 변형해서 사용하면 될 듯하다. 보통 아래 내용을 고려한다고 한다.

  • Cache 저장소와 DB 저장소 간 처리 패턴
  • 생성 및 제거
  • 동시성 이슈
  • 고가용성을 고려

각 항목을 간단하게 정리한다.

고가용성을 고려

캐시는 기본적으로 성능을 개선하는데 목적을 둔다. 캐시는 읽기에 집중하는 것이 성능 확보 측면에서 핵심 고려사항이 될 수 있다.

또한 캐시 서버또한 프로세스이므로 down을 고려해야 한다. 캐시 서버도 이중화를 하거나 캐시 down 시 DB에서 데이터를 조회 할 수 있도록 예외를 처리해야 한다. 즉 캐시 서버가 다운 되더라도 서비스 제공에 지장이 있으면 안된다는 의미

동시성 이슈

캐시에 보유하는 데이터를 수정해야 될 경우가 종종 발생한다. 그럴 경우 변경에 대해서 다른 처리 로직에서 영향을 받아서는 안된다. 이는 데이터의 종류에 영향을 받는다. 가령 조회수 같이 조회 시 1씩 증가하는 데이터의 경우는 조회와 동시에 1을 업데이트 하면된다. 하지만, DB의 영속데이터와 싱크가 필요한 캐싱 데이터의 경우에는 1을 더해서 업데이트 하는 것과는 다르게 더 복잡한 로직이 필요할 것이다.

보통 위의 경우 캐시 데이터를 업데이트 하기 전에 Lock을 잡는 방식(분산 Lock)을 쓴다. Lock이 잡힌 동안에는 DB를 사용하게 할 수 있다. 아니면 Lock을 잡는 시간을 짧게 짧게 잡아서 업데이트를 자주 하는 방식도 있다. 이는 데이터의 종류에 따라서 적절한 방식을 사용해야 될 것으로 판단 된다.

생성 및 제거

캐시는 자주 사용되나 자주 변경되지 않는 데이터를 기준으로 하는 것이 좋다. 또한 휘발성을 기본으로 하기 때문에 데이터의 유실을 고려해야 된다. 따라서 데이터 중 중요 정보, 민감 정보는 저장하지 않는 것이 좋다.

캐시는 DB에 저장된 데이터의 복사본인 경우가 많다. 따라서 메모리 사용량이 증가하는데, 그렇기에 주기적으로 만료정책(Expire Time — TTL)을 적용하여 삭제를 할 수 있다. 기본적으로 LRU 알고리즘을 기반으로 데이터를 제거한다.

Cache 저장소와 DB 저장소 간 처리 패턴

링크 블로그에서 정리가 잘 되어있는데 보통 4가지 패턴이 있다. Cache Aside 패턴, Write Back 패턴, Read Through 패턴, Write Through 패턴이 있다.

  • Cache Aside

cache aside 패턴은 캐시로 부터 빠르게 데이터를 조회해 올 수 있도록 설계하는 방식이다. 먼저 캐시에 있는지 확인한다. 없으면 DB를 조회하고 캐시에 업데이트를 한다.

이 패턴의 장점은 읽기 성능에 매우 뛰어나며, 캐시가 장애가 나도 DB에서 읽어가면 되기에 고가용성이 발생한다.

다만 단점은 동시성 이슈나 정합성의 문제가 발생한다.

  • Write Back 패턴

(약간 이벤트 소싱 패턴 같기도 하고….)

해당 패턴은 캐시에 먼저 데이터를 저장하고 그 데이터를 DB에 저장하는 것이다. 일종의 캐시가 Queue 역할을 한다.

장점은 DB의 장애 발생 시 복구까지 시간을 좀 벌 수 있다. 그리고 캐시와 DB간의 데이터 정합성을 맞추기가 쉽다.

단점은 명확하다. 캐시가 뻗으면 데이터가 저장되지 못한다. 그리고 불필요한 리소스가 많이 저장 될 수 있으며, write 행위가 리소스를 많이 잡아 먹는다.

  • Read Through

Cache Aside 패턴과 유사한데, 캐시 miss가 발생하는 경우 데이터를 DB에서 cache쪽으로 저장한다.

  • Write Through

write back 패턴과 유사한 방식인데, 차이점은 write back 패턴에서는 DB에 일정시간 이후 update를 하지만 write through 패턴에서는 바로 업데이트를 한다. ( 참고 링크 )

캐시 장애 복구 방식

사실 이 부분은 사용하는 production에 따라 차이는 있지만 크게 개념적으로 공통적인 부분만 작성을 한다. 내가 redis를 가지고 구현을 했기 때문에 아마도 redis의 내용을 쓰게 될 듯하다.

  • snap shot ( Redis에서 RDB )

이 방식은 일정 시점까지 스냅샷을 파일로 저장하는 방식이다. 저장된 파일로 백업이 용이하고 스냅샷 시점을 기준으로 복구를하기 때문에 복구가 빠르다.

다만 스냅샷을 찍는 시점에서는 데이터 유실가능성이 있다.

  • write operation ( Redis에서 AOF )

이 방식은 모든 write 행위에 대한 기록을 남기고 장애가 생기면 모든 기록을 replay하는 방식이다.

장점은 모든 데이터를 완벽하게 복구가 가능하다. 다만 단점은 저장해야 되는 데이터 크기가 커진다.

위 내용은 레디스 공식 홈페이지를 참고했다.

*RBS = RedisDatabase / AOF = Append Only File

정리

요약하면 캐시는 임시 저장이다. 데이터의 영속성을 보장하지 못하고 영속 데이터와는 정합을 맞춰야 되기 때문에 그와 관련 된 구현 패턴이 존재한다.

캐시의 모든 기능을 사용 할 수는 없겠지만, 간단하게 cache aside 패턴을 적용해보고 성능 개선이 얼마나 되는지 다음글에서 정리해보겠다.

또한 사용하지 못하는 부분이나, 이슈는 별도 정리하고자 한다.

--

--