캐시
캐시는 데이터를 임시로 저장해 두는 임시 저장 장소를 의미한다. 모든 저장 장치는 각자 저장 속도나 읽기 속도가 다른데, 주로 좀더 빠른 저장이나 읽기 속도를 가졌지만 좀더 용량이 적거나 저장 시간이 적은 저장 장치를 캐시 역할로 둔다. 임시적으로 데이터를 저장해 두고 빠르게 읽기/쓰기를 하기 위해 캐시를 이용한다.
간단하게 예를 들자면 우리 컴퓨터의 SSD 와 RAM 메모리가 있다. 같은 가격대비 읽기쓰기 속도는 RAM 메모리가 더 빠르지만, 용량은 SSD 가 더 크고, RAM 메모리는 컴퓨터를 재부팅 시에 데이터가 휘발된다는 점이 있다.
컴퓨터에서 엑셀을 실행시키고 스프레드시트를 작성하고, 읽는 과정에서 실시간으로 작업중인 데이터가 있다고 치자. 이 상황에서 데이터를 하나씩 읽고 작성할 때 마다 SSD 에 접근하게 된다면 속도가 느린것은 물론이거니와 SSD의 수명을 꽤나 깎아먹게 된다. 그렇기 때문에 읽고 쓰기 위해서는 데이터는 모두 RAM 메모리에 올라가게 되고, 해당 공간에서 데이터 수정과 읽기가 진행되다가 우리가 '저장' 버튼을 누르거나 자동 저장 기능이 수행될 경우에는 SSD 에 저장이 수행된다.
엑셀을 종료한 뒤, 파일 시스템에서 저장했던 엑셀 파일을 다시 불러 올 경우에는 SSD 에서 데이터를 읽어서 RAM 메모리에 적재하고, 위와 같은 과정을 반복하게 된다.
백엔드 시스템에서의 캐시
데이터의 읽기/쓰기 요청을 네트워크에서 수행하는 백엔드 서버에 시스템에도 캐시가 존재한다. 인프라 각각의 자체 관리 캐시도 있고 인프라를 이용하는 개발자가 구성하고 운영하는 캐시도 있는데, 이 포스트에서는 후자인 개발자가 구성하고 관리하는 캐시 시스템에 대해 논의하고자 한다.
API 서버의 캐시 역할
API 서버는 여러 사용자로부터 지속적으로 요청을 처리하는데, 이 과정에서 동일하거나 유사한 요청이 반복적으로 발생할 수 있다. 만약 매번 요청이 들어올 때마다 데이터베이스(DB) 또는 외부 API에 접근해서 데이터를 가져온다면, 시스템의 성능이 저하되고 응답 시간이 길어지게 된다. 이런 문제를 해결하기 위해 API 서버에서는 캐시를 활용하여 다음과 같은 이점을 얻는다.
1. 빠른 응답 속도 제공: 자주 조회되는 데이터를 캐시에 저장해두고, 이후 동일한 요청이 들어올 경우 캐시에서 데이터를 즉시 반환함으로써 응답 시간을 크게 단축할 수 있다.
2. 데이터베이스 및 서버 부하 감소: 데이터베이스나 외부 API에 대한 반복적인 요청을 줄여서 서버 자원의 부하를 줄이고, 시스템의 안정성과 확장성을 높인다.
캐시 구현 시 고려 사항
1. 캐시 만료 시간 설정: 캐시 데이터를 오래 유지할 경우 실시간 데이터와의 불일치 문제가 발생할 수 있다. 적절한 만료 시간을 설정해야 한다.
2. 캐시 정합성: 데이터가 변경될 때 관련 캐시 데이터를 갱신하거나 무효화하는 전략이 필요하다.
3. 캐시 히트율 관리: 캐시가 얼마나 효율적으로 사용되고 있는지 모니터링하고, 캐시 히트율을 높이기 위한 최적화 작업이 필요하다.
4. 분산 캐시 고려: 여러 서버에서 동일한 캐시를 공유할 경우, Redis와 같은 분산 캐시 솔루션을 사용하여 캐시 일관성을 유지할 수 있다.
캐시 전략 4가지
캐시 전략은 데이터를 언제, 어떻게 캐시에 저장하고 갱신할지를 결정하는 방식이다. 각 전략은 서로 다른 상황에 적합하며, 다양한 방식으로 시스템 성능을 최적화할 수 있다. 여기서는 주요 캐시 전략 4가지를 소개한다.
1. Read Through
• 설명: 애플리케이션이 데이터를 읽을 때 먼저 캐시를 조회하고, 캐시에 데이터가 없을 경우 데이터베이스(DB)에서 가져온 후 캐시에 저장하는 방식이다. 이후 동일한 요청이 들어오면 캐시에서 즉시 반환한다.
• 활용 상황: 주로 자주 조회되지만 변경 빈도가 낮은 데이터에 적합하다. 예를 들어, 상품 목록이나 카테고리 정보 등의 데이터에 활용할 수 있다.
• 장점: 캐시와 DB 간의 동기화를 자동으로 처리하며, 애플리케이션 코드가 단순해진다
2. Write Through
• 설명: 애플리케이션이 데이터를 변경할 때 먼저 캐시에 쓰고, 동시에 데이터베이스에도 데이터를 갱신하는 방식이다.
• 활용 상황: 데이터 일관성이 중요하고, 데이터 변경이 자주 발생하지 않는 경우에 적합하다. 예를 들어, 사용자 프로필 정보와 같은 데이터에 적용할 수 있다.
• 장점: 캐시와 데이터베이스가 항상 최신 상태를 유지한다.
3. Write Behind (Write Back)
• 설명: 데이터를 변경할 때 먼저 캐시에 저장하고, 데이터베이스에는 일정 시간 뒤에 비동기적으로 쓰는 방식이다. 이로 인해 데이터베이스에 대한 쓰기 부하가 줄어들지만, 캐시와 DB 간 일시적 불일치가 발생할 수 있다.
• 활용 상황: 쓰기 작업이 빈번하지만, 즉각적인 일관성이 필요하지 않은 데이터에 적합하다. 예를 들어, 로그 데이터나 비정형 데이터 저장에 활용할 수 있다.
• 장점: 데이터베이스에 대한 부하를 줄이고, 쓰기 성능을 향상시킨다.
4. Cache Aside
• 설명: 애플리케이션이 직접 캐시를 관리하는 방식으로, 먼저 캐시를 조회하고 데이터가 없을 경우 DB에서 데이터를 가져와 캐시에 저장하는 패턴이다. 데이터 변경 시에도 애플리케이션이 캐시 무효화를 직접 처리한다.
• 활용 상황: 캐시 갱신 정책을 애플리케이션이 세밀하게 제어해야 할 때 사용된다. 예를 들어, 사용자별 맞춤 데이터를 캐시에 저장할 때 적합하다.
• 장점: 캐시 정책을 유연하게 설정할 수 있으며, 필요에 따라 캐시 데이터를 세밀하게 조작할 수 있다.
캐시 스탬피드(Cache Stampede) 현상
• 설명: 캐시가 만료되거나 초기화될 때, 여러 클라이언트가 동시에 동일한 데이터를 요청하면서 대량의 요청이 한꺼번에 데이터베이스로 몰리는 현상이다. 이로 인해 서버가 과부하에 걸리거나 데이터베이스 성능이 크게 저하될 수 있다.
• 해결 방안:
1. Locking: 캐시에 데이터가 없을 경우 첫 번째 요청만이 DB에 접근하도록 잠금을 설정하여 나머지 요청들은 대기시킨다.
2. Lazy Expiration: 캐시가 만료될 때, 요청을 처리하면서 비동기적으로 캐시를 갱신한다.
3. 캐시 만료 시간 분산(Randomized TTL): 데이터마다 만료 시간을 랜덤하게 설정하여 동일 시간에 캐시가 대거 만료되는 것을 방지한다.
캐시 스탬피드는 대규모 트래픽을 처리하는 시스템에서 빈번하게 발생할 수 있으므로, 이러한 해결책을 적용하여 시스템 성능 저하를 예방하는 것이 중요하다.
캐시 적용 방안
현재 진행 중인 미니 이커머스 프로젝트에 캐시를 적용하는 방안을 선정해 보았다. 사용가능한 캐시 서버는 Redis를 사용하는 것을 전제로 이야기 하겠다.
인기 상품 목록 캐시
• 적용 시나리오: 사용자들이 가장 자주 조회하는 데이터 중 하나가 상품 목록이다. 이 데이터를 캐시에 저장해 두면 매번 DB에 접근하지 않고 빠르게 응답할 수 있다. 현재 인기 상품 목록 API의 경우 쿼리가 무거운 편이여서 캐시를 적용했을 때의 이점이 도드라질 것으로 보인다.
• 적용 방법: 상품 목록 조회 API에 캐시를 적용한다. 요구사항에 따라 특정 일자에 대한 인기상품을 조회하는 경우도 있는데 해당 쿼리 파라미터를 함께 key 값으로 하는 등의 방식으로 캐싱이 가능하다.
Local Cache VS Redis Cache
멀티 인스턴스에서의 캐싱을 한다고 하면, Local Cache 같은 경우 일시적으로 같은 요청에 대한 다른 응답이 갈 가능성이 있다. 인기 상품 목록 캐시같은 경우는 같은 유저가 두번 조회할 경우 갑자기 인기 상품에 변화가 왔다 갔다하면 혼란스러울 수 있기 때문에 캐시 서버를 이용하기로 한다
• 갱신 정책: 해당 기능 같은 경우 자주 변경되지 않아도 되는 데이터이다. 현재로써는 Read-Through 로 12시간 정도의 단위로 캐시가 소멸되는 정도가 적당할 듯하다.
'개발자의 길' 카테고리의 다른 글
[MSA] 분산 환경에서의 트랜잭션 처리 - 분산 트랜잭션과 보상 트랜잭션 (1) | 2025.02.13 |
---|---|
[Database] MySQL 에서 Index 를 설계하고 추가하는 방법 (0) | 2025.02.12 |
[항해플러스] 미니 e-commerce 프로젝트에서 발생가능한 동시성 문제와 그 해결법 (0) | 2025.01.19 |
[Infra] Docker (1) - Docker Engine (0) | 2024.04.14 |
[Java] Boxing 과 Unboxing (0) | 2024.01.22 |