[Web] Cache 전략
Cache
간단하게 하위 레벨의 메모리에 저장 되어있는 데이터에는 접근 시간이 오래걸리기 때문에빈번하게 사용되는 데이터들에 대해서 상위 레벨의 메모리에 저장해두고 데이터 접근 시간을 줄이는 기법이다. 로우레벨로 내려가면 운영체제 레벨에서도 CPU, GPU 성능 향상을 위해 캐시를 이용하며 어플리케이션 레벨에서 역시 빈번하게 사용되는 데이터에 대해 동일한 방식을 적용하여 성능을 향상시킨다. WS 와 WAS를 분리하는 목적 중 하나가 정적 데이터들에 대한 캐시 기능을 제공하는 목적이 있다.
Web Application 계층에서의 캐시
자체적으로 어플리케이션의 메모리 영역에 캐시를 하는 방식도 있지만, 멀티 인스턴스로 동작하는 Web Application들의 특성상 Redis와 같은 인메모리 데이터베이스를 이용하는것이 일반적이다.
Cache 전략
캐시 전략은 쓰기 전략과 읽기 전략으로 나뉜다.
쓰기 전략
- Write Back Write Back은 모든 데이터를 캐시 스토리지에 먼저 저장하고, 스케쥴러 등을 통해 배치 작업으로 데이터베이스에 반영하는 방식이다. 따라서 실제 데이터와 캐시 데이터 사이의 불일치가 발생한다. 빈번한 쓰기 작업이 이루어고, 읽기 작업에 많은 서버 리소스가 소요된다면 고려해볼 수 있는 전략이다.

이런 방식의 쓰기 전략의 경우, 캐시 스토리지에 장애가 발생할 경우 데이터가 유실될 수 있기 때문에 유실되선 안되는 데이터에 대해 이러한 전략을 적용하려 할 경우 캐시 스토리지의 HA 구성이 필요하다.
- Write Through Write Through 패턴은 데이터베이스와 캐시 스토리지에 동시에 데이터를 저장하는 방식이다. 캐시 스토리지와 데이터베이스에 동일한 데이터가 동기화되어 있다는 장점이 있다. 하지만 대부분의 서비스에서 일부 데이터에 대해 전체 접근량의 상당수가 몰리는 경우라면 캐시 스토리지의 낭비로 이어질 수 있다.
3. Write Around
모든 Command 요청 의한 변경 사항은 데이터베이스에만 반영한다. 읽기 작업에서 Cache Miss가 발생했을 경우에만
캐시 스토리지에 데이터를 저장한다. Write Through와 달리 캐시에 저장하는 과정은 생략되기 때문에 조금 더 빠르지만
캐시 데이터와 데이터베이스의 불일치가 발생한다. 그렇기 때문에 Command 요청을 처리하고 난 뒤 캐시에 저장된 데이터를 삭제하거나
변경해야하는 작업이 필요하다.

읽기 전략
- Look Aside
데이터 접근 시 캐시를 우선적으로 확인한 뒤, 캐시가 없을 경우 데이터베이스에서 조회하고 그 데이터를 다시 캐시 스토리지에 저장한다. 가장 일반적으로 사용되며, 반복적인 읽기 요청이 많은 서비스에 적합하다. 캐시 스토리지 서버와 데이터베이스 서버가 분리되어 있기 때문에 캐시 스토리지에 장애가 발생하더라도 읽기 작업은 데이터베이스에서 이루어지기 때문에 장애 상황에 강하다.
다만 캐시 스토리지 서버에 많은 Connection이 생성된 상황에서 캐시된 데이터가 삭제될 경우 Cache Stampede 현상이 일어날 수 있기 때문에 TTL을 늘리는 등의 설정이 필요하다.

- Read Through 캐시에서만 데이터를 읽어오는 전략이다. Look Aside와 차이점이라고 하면, 데이터 동기화의 주체가 어플리케이션 서버가 아닌 캐시 프로바이더에게 위임한다는 점이다. 데이터 조회 자체를 캐시 프로바이더에게 넘기고, 프로바이더 내부에서 캐시가 존재하면 즉시 반환 그렇지 않다면 프로바이더에서 데이터베이스를 직접 조회한다.
이 방식은 데이터 동기화 문제로부터 자유롭지만 Look Aside와 달리 캐시 스토리지와 데이터베이스가 결합되어 있고, 읽기 작업 자체가 추상적으로는 캐시에서만 읽는 형태이기 때문에 캐시 스토리지에 에러가 발생할 경우 전체 서비스 중단으로 이어질 가능성이 있다.

이 웹사이트의 적용
이 홈페이지의 백엔드 서버(NestJS 버전 기준) Write Around + Read Around 전략을 이용하고 있다. 개인 홈페이지의 특성상 Write는 내가 글을 쓰는 경우를 제외하면 일어날 일이 없고 댓글 서비스조차 막아둔 상황이라 사실 읽기 작업이 대부분이기 때문에 위와 같은 전략을 선택했다.
개인 기록용으로 사용하는 홈페이지를 클라우드 서비스를 이용하기도 애매하고, 라즈베리파이를 이용하여 구동하는데 SD카드 사망 문제를 방지하기 위해 루트 파일 시스템조차 램에서 동작하도록 변경하였고, 데이터베이스만 따로 하드디스크를 연결해두고 조회 자체를 레디스에서 하도록 변경하였다. 실제 1년간 운영 로그를 살펴본 결과 대부분의 요청은 읽기 요청이었기 때문에 하드디스크에 대한 IO작업은 거의 일어나지 않았다.
현재 Spring 기반으로 백엔드를 교체하는 중인데, 여기서 캐시를 적용하는 경우 현재 방식과 조금 달라질 수 있을 것 같다.