[아키텍처/소프트웨어] 캐싱

로컬 캐싱과 글로벌 캐싱

캐싱은 캐싱 데이터를 어느 곳에 위치시킬 것인지에 따라 크게 로컬 캐싱(지역 캐싱)과 글로벌 캐싱(전역 캐싱)으로 구분할 수 있다. 로컬 캐싱(local caching)이란 캐싱을 위해 분리된 별도의 캐싱 레이어 없이 단일 애플리케이션 내에 캐싱 데이터를 저장하는 것을 의미한다. 원본 데이터를 데이터베이스에서 조회하고 별도의 처리 없이 그대로 캐싱하거나, 추가적으로 처리하여 특정 형태로 캐싱할 수 있다. 로컬 캐싱을 사용할 경우 캐싱 레이어로의 추가적인 요청이 없으므로 네트워크 트래픽 및 관련 오버헤드가 없으며 따라서 캐싱 데이터에 빠르게 접근할 수 있다. 로컬 캐싱의 경우 데이터의 저장 위치 및 데이터의 유효성이 특정 애플리케이션에 한정되므로 시스템이 한 대의 서버로 구성된 경우 또는 데이터 조회 요청이 많지 않거나 데이터의 크기가 상대적으로 작은 경우에 적합하다.

로컬 캐싱은 클라이언트 사이드 캐싱과 서버 사이드 캐싱으로 구분할 수 있다. 클라이언트 사이드 로컬 캐싱의 경우 데이터를 제공 받는 클라이언트가 자주 사용하는 데이터를 로컬 캐싱하여 동일한 데이터에 대한 반복적인 요청을 줄인다. 이를 통해 네트워크 트래픽과 내부 처리 속도를 향상시킬 수 있다. 클라이언트 사이드 로컬 캐싱의 예로는 웹 브라우저나 네이티브 모바일 애플리케이션의 정적 파일 캐싱이 있다. 서버 사이드 로컬 캐싱의 경우 데이터를 제공하는 서버가 클라이언트에게 자주 제공하는 응답 데이터를 로컬 캐싱하여 동일한 요청에 대한 응답 데이터를 요청마다 처리하는 대신 미리 캐싱된 데이터를 빠르게 제공한다. 이를 통해 서버의 부하를 줄이고 응답 시간을 높일 수 있다. 서버 사이드 로컬 캐싱의 예로는 백엔드 API 서버의 데이터베이스 데이터 캐싱이 있다. 마이크로서비스 아키텍처 시스템 상에서 한 서버는 또다른 서버에게 데이터를 요청하는 클라이언트가 되기도 하기 때문에 어떤 데이터를 어느 곳에서 위치시키고 이를 캐싱할지가 중요하다.

로드 밸런서가 클라이언트의 요청을 여러 대의 서버로 분산시키는 분산 시스템(distributed system) 구조에서 클라이언트의 요청 부하는 여러 서버로 분산된다. 이때 로드 밸런서가 고정 세션(sticky session) 기능을 제공하지 않는 경우 특정 클라이언트의 요청이 한 서버로만 이루어지는 것이 보장되지 않는다. 따라서 로컬 캐싱을 사용하는 서버들로 구성된 분산 시스템 구조에서 서버 간 동기화되지 않은 캐싱 데이터로 인해 클라이언트는 데이터의 일관성을 보장 받지 못한다. 또한 로컬 캐싱은 애플리케이션 내에 캐싱 데이터를 저장하는 것이므로 장애 또는 유지보수로 인해 애플리케이션이 재시작 또는 종료되는 경우 캐싱된 데이터가 삭제되는 단점이 존재한다. 이 경우 클라이언트는 서버로부터 새로운 데이터를 응답으로 받고 다시 캐싱하는 과정이 필요하다.

글로벌 캐싱(global caching)이란 캐싱을 위해 분리된 별도의 캐싱 레이어를 사용하여 캐싱 데이터를 별도의 서버에 저장하는 것을 의미한다. 캐싱 서버의 개수에 관계 없이 캐싱 데이터는 동기화되어 클라이언트에게 데이터 일관성을 제공한다. 글로벌 캐싱을 사용할 경우 캐싱 레이어로의 추가적인 요청이 수행되어 네트워크 트래픽 및 관련 오버헤드가 발생하므로 로컬 캐싱에 비해 캐싱 데이터에 접근하는 속도는 상대적으로 느리다.

로컬 캐싱과 글로벌 캐싱을 구분하는 요소는 캐싱 데이터의 단일 애플리케이션에 저장 여부, 다중 애플리케이션의 경우 동기화 저장 여부이다. 서버가 데이터를 자체적으로 생성하여 클라이언트에게 응답으로 제공하는 경우, 해당 데이터를 로컬 캐싱한다면 서버는 캐싱 서버가 된다. 클라이언트 입장에서 로컬 캐싱 서버가 제공하는 캐싱은 글로벌 캐싱이지만 다중 서버 구성인 경우 데이터의 일관성 보장을 위해 서버 간 캐싱 데이터의 동기화가 이루어져야 한다.


분산 캐싱

분산 캐싱(distributed caching)이란 네트워크 상에서 서로 분리된 여러 애플리케이션에 캐싱 데이터를 저장하고 서로 공유하는 것을 의미한다. 분산 캐싱은 데이터 조회 요청이 많거나 데이터 크기가 큰 경우 캐시 서버의 부하를 분산시키기도 하지만 서버들을 물리적 및 지리적으로 다르게 위치시켜 클라이언트와 가장 가까운 서버가 보다 낮은 지연 시간 및 빠른 응답 속도로 데이터를 응답하는 장점도 제공한다.

분산 캐싱의 경우 다중 서버가 데이터를 제공하므로 단일 캐싱 서버 보다 더 높은 수준의 시스템의 내결함성, 확장성, 성능을 제공할 수 있다. 분산 시스템에서 분산 캐싱은 데이터의 일관성을 보장해야 한다. 즉, 서로 다른 서버에 대한 요청이 이루어지더라도 클라이언트에게 응답에 대한 일관성을 보장해야 한다.

분산 캐싱 방법 중 하나인 분산 메모리 캐시는 실행 중인 애플리케이션의 메모리에 데이터를 저장한다. 데이터 크기에 따른 메모리 점유 부하가 문제가 적은 경우 고려해볼 수 있다.


캐시 스탬피드

서버 애플리케이션이 데이터의 캐싱 처리를 수행하는 경우 서버는 먼저 로컬 캐시 데이터 저장소로부터 데이터를 조회한 후 데이터가 있다면 해당 값을 응답하고, 데이터가 없다면 원본 데이터 저장소(예: 데이터베이스)에서 조회하거나 데이터를 계산 및 생성한 후 로컬 저장소에 저장한다. 요청에 대해 캐싱된 데이터를 응답으로 제공하는 경우를 캐시 히트(cache hit)(또는 캐시 적중), 캐싱된 데이터가 없어 원본 데이터를 다시 조회하거나 연산 처리하여 캐싱 데이터를 다시 채우는 경우를 캐시 미스(cache miss)라고 한다. 캐싱할 데이터를 생성하고 캐시 저장소에 저장하는 캐시 연산(cache computation) 또는 캐시 채우기(cache population) 처리 과정이 얼마나 빈번하게 일어나는지는 서버 부하와 관련이 있다.

서버가 동일한 요청을 지속적으로 받는 경우 캐싱 처리는 첫 요청에 대해 한 번만 수행되며 이후 요청에 대해 서버는 캐싱된 데이터를 응답하므로 요청량이 많더라도 낮은 부하와 빠른 응답 속도로 요청을 처리할 수 있다. 이는 서버 사이드 캐싱 사용의 장점이기도 하다. 이후 다른 새로운 요청이 유입되는 경우에도 해당 요청에 대한 캐싱 처리가 동일하게 수행되고 이후 요청은 모두 캐시 히트를 발생시킨다. 그러나 한 대의 서버가 멀티 스레드를 사용하여 캐시 미스가 발생한 요청에 대한 캐싱 처리를 동시적으로(concurrently) 수행하거나 병렬 연산 시스템에서 여러 서버 애플리케이션(프로세스)이 병렬적으로(parallelly) 수행하는 경우, 스레드(또는 프로세스) 간 캐싱 처리에 대한 적절한 재처리 완화 및 방지 과정이 없다면 동일한 캐싱 데이터에 대한 조회 또는 연산, 저장 과정이 동시에 반복적으로 일어날 수 있으며 이는 서버 및 원본 데이터 저장소에 부하를 일으킨다. 이러한 환경에서 캐싱을 처리하는데 소요되는 시간 간격 사이에 서버가 받은 요청은 동시적 캐싱 처리를 유발시키며 이는 시스템의 연쇄적 장애로 이어질 수 있다. 이를 캐시 스탬피드(cache stampede) 또는 캐시 미스 스톰(cache miss storm)이라고 한다.

동일한 데이터를 반복적으로 캐싱 처리하는 과정을 방지하기 위한 여러 방법이 있다. 서버가 캐싱 처리를 직접 수행하는 경우 요청에 대한 캐시 미스가 발생하면 스레드(또는 프로세스)는 해당 캐시 데이터에 대한 락(lock)을 획득하려고 시도한 후 이를 획득한 경우에만 처리하도록 하는 락킹 과정을 수행하여 동시 처리 자체를 막을 수 있다. 이외에 스레드(또는 프로세스) 별로 캐시 처리를 수행하되 수행 횟수를 확률적으로 감소시키는 캐시 데이터의 확률적 조기 만료(probabilistic early expiration) 방법, 요청 처리 스레드(또는 프로세스)가 캐싱 처리를 직접 수행하는 대신 별도의 스레드(또는 프로세스)를 두는 방법 등이 있다.


Nginx 캐시 클러스터


바이트 배열 데이터

이미지나 영상 콘텐츠 데이터를 캐싱하는 경우 리소스 출처로부터 제공 받은 이진(binary) 데이터인 바이트 배열 전체를 캐싱하거나 리소스 출처 경로(URL)를 캐싱한다. 바이트 배열을 캐싱하는 경우 클라이언트는 캐싱된 바이트 배열 데이터를 응답으로 받고 미디어 타입에 맞게 변환 처리하여 사용한다. 리소스 출처 경로를 캐싱하는 경우 클라이언트는 응답 받은 해당 URL에 대한 추가 요청을 보낸 후 바이트 배열 데이터를 응답으로 받고 미디어 타입에 맞게 변환 처리한다. 일반적으로 원본 바이트 배열 데이터 보다는 압축이나 크기 및 해상도 변경, 확장자 변경 등에 의해 특정 형태로 변환된 데이터를 캐싱하고 서버의 메모리나 파일 시스템에 저장한다.

웹에서 CORS를 허용하기 위해 이미지나 영상 데이터를 리소스 출처 대신 클라이언트에게 제공하는 프록시 서버의 경우 바이트 배열을 직접 클라이언트에게 응답으로 제공할 수 있다. 이때 바이트 배열 응답 데이터를 캐싱하는 경우 메모리나 파일 저장 공간을 점유하게 되어 리소스 부하가 발생할 수 있으므로 적절한 데이터 변환 과정이 필요하다. 변환된 데이터를 캐싱하거나 적절한 압축 기술을 사용하여 리소스 점유를 최적화할 수 있다.


참고

Comments