(번역) 어떻게 Facebook 은 Memcached 를 사용해서 초당 수억건의 요청을 처리하는가
(번역) 어떻게 Facebook 은 Memcached 를 사용해서 초당 수억건의 요청을 처리하는가
Facebook 규모로 소셜 네트워킹을 운영하기 위해서는 알아야 하는 2가지의 절대적인 사실이 있습니다.
- 첫 번째로, 서비스의 규모가 내려가서는 안됩니다.
- 둘째로, 서비스는 느려져서는 안됩니다.
이 두 요소는 사람들이 당신의 소셜 네트워크에 머무를지 아닐지를 결정하는 요소들입니다.
사용자들이 상호 연결되어 있기 때문에, 심지어 몇 명의 사람들도 떠나는 것은 전체 사용자 기반에 영향을 미칩니다. 대부분의 사람들은 그들의 친구나 친척들이 온라인에 있고, 그들의 플레이에 도미노 효과가 있기 때문에 온라인에 있습니다. 문제로 인해 한 사용자가 중단되면, 다른 사용자들도 떠날 가능성이 있습니다.
Facebook 은 그 자체의 인기 때문에 이런 문제들을 일찍히 해결해야만 했습니다. 어느 시점에서든 수백만 명의 사람들이 전 세계에서 Facebook에 접속하고 있습니다.
소프트웨어 설계 측면에서 이는 다음과 같은 몇개의 요구 사항을 의미합니다.
- 페이스북은 실시간 커뮤니케이션을 지원해야 합니다.
- 페이스북은 망설임 없이 콘텐츠를 집계해서 보여줄 수 있는 능력을 갖추어야 합니다.
- 수십억 명의 사용자 요청을 처리할 수 있어야 합니다.
- 여러 지리적 위치에 걸쳐서 수조개의 아이템을 저장할 수 있어야 합니다.
이런 목표를 달성하기 위해서 Facebook 은 Memcached 의 오픈 소스 버전을 채택하고, 이를 강화하여 분산 키 밸류 저장체계를 만들었습니다.
이 게시물에서는 페이스북이 초당 수십억 건의 요청을 처리하기 위해서 Memcached 를 확장하는 데 있어서 여러 문제를 어떻게 처리했는지 살펴볼 것입니다.
Memchaced 소개
Memcached 는 할당, 가져오기, 삭제와 같은 명령어 집합을 처리하기 위한 인메모리 키-밸류 저장소입니다.
오픈 소스 버전의 경우에는 단일 머신으로 인 메모리 해시 테이블을 제공했습니다. Facebook 의 엔지니어들은 이 버전을 기본 구성 요소로 차용해서, Memcache 로 알려진 분산 키-밸류 스토어를 만들었습니다.
즉, Memcached 는 소스 코드 또는 실행되는 바이너리이며, Memcahce 는 그 뒤를 지탱하는 분산 시스템을 의미합니다. 기술적으로 Facebook 은 Memcache 를 2가지 방법으로 사용했습니다.
Query Cache
쿼리 캐시의 역할은 데이터베이스의 부하를 줄이는 것입니다. 이 방법으로, Facebook 은 Memcached 를 요청시마다 채워지면서, 메인 데이터 소스(db) 앞에서 데이터를 처리하는 구조로 사용했습니다. 이미 cache-aside 패턴이라는 이름으로 유명한 방식입니다.
하단의 diagram 은 look-aside 캐시 패턴이 어떻게 읽기와 쓰기에 대응하는지를 보여줍니다.
읽기 요청 시, 요청이 발생할 때마다 채워진 캐시를 활용합니다. 즉, 데이터는 클라이언트에서 데이터를 요청할 때 오직 한번만 로드된다는 것입니다.
요청을 처리하기 전에, 클라이언트는 캐시를 먼저 체크합니다. 만약 캐시 안에 원하는 데이터가 존재하지 않는다면(cahe-miss), 클라이언트는 데이터를 db 에서 가져오고, 캐시를 업데이트합니다.
쓰기 요청은 데이터를 업데이트하기 위해서 더 흥미로운 접근 방식을 취합니다. 특정 키가 데이터베이스에서 업데이트된 이후에, 시스템은 캐시에서 일치하는 값을 직접적으로 업데이트하지 않습니다. 대신에, 해당 키에 대한 데이터 자체를 캐시에서 완전히 제거합니다. 이 과정을 캐시 무효화라고 합니다.
캐시 집합을 무효화함으로써, 시스템은 클라이언트가 다음번에 해당 키에 대한 데이터를 요청할 때 캐시 미스를 일으키도록 하고, 데이터베이스로부터 직접 가장 최신의 값을 가져오도록 강제합니다. 이러한 접근법을 통해서 캐시와 데이터베이스 사이에 데잍 일관성을 유지할 수 있습니다.
일반적인 캐시
Facebook은 또한 Memcache를 범용 키-밸류 저장소로 활용합니다. 이를 통해 조직 내 여러 팀이 계산 비용이 많이 드는 기계 학습 알고리즘으로부터 생성된 계산 결과를 저장하기 위해 Memcache를 활용할 수 있습니다.
미리 계산된 이러한 ML 결과를 Memcache에 저장함으로써 다른 응용 프로그램은 필요할 때 언제든지 빠르고 쉽게 액세스할 수 있습니다. 이 접근 방식은 향상된 성능 및 리소스 최적화와 같은 몇 가지 이점을 제공합니다.
Facebook 의 고수준 아키텍쳐
Facebook의 아키텍처는 플랫폼의 거대한 규모와 글로벌 서비스를 처리하기 위해 구축되었습니다. Memcache를 채택할 당시 Facebook의 고수준 아키텍처는 다음 세 가지 주요 구성 요소로 구성되었습니다
1. 지역성
페이스북은 전략적으로 전 세계의 다양한 위치에 서버를 배치합니다. 이 리전들은 두 가지 유형으로 분류됩니다:
주요 Region: 주요 Region은 대부분의 사용자 트래픽과 데이터 관리를 담당합니다.
보조 Region: 여러 보조 리전이 전 세계적으로 분산되어 서로 다른 지리적 영역의 사용자에게 중복성, 로드 밸런싱 및 향상된 성능을 제공합니다.
주요 또는 보조 리전에 관계없이 각 리전에는 여러 개의 프론트엔드 클러스터와 단일 스토리지 클러스터가 포함됩니다.
2. 프론트엔드 클러스터
Facebook은 각 리전 내에서 프론트엔드 클러스터를 사용하여 사용자 요청을 처리하고 콘텐츠를 제공합니다. 프론트엔드 클러스터는 다음과 같은 두 가지 주요 구성 요소로 구성됩니다:
웹 서버: 이러한 서버는 사용자 요청을 처리하고 페이지를 렌더링하며 사용자에게 콘텐츠를 전달하는 역할을 합니다.
Memcache Servers: Memcache 서버는 분산 캐싱 계층의 역할을 하며, 빠른 검색을 위해 자주 액세스하는 데이터를 메모리에 저장합니다.
프론트엔드 클러스터는 수요에 따라 수평으로 확장되도록 설계되었습니다. 사용자 트래픽이 증가하면 클러스터에 웹 및 멤캐시 서버를 추가하여 증가된 부하를 처리할 수 있습니다.
3. 저장소 클러스터
각 지역의 핵심부에는 저장소 클러스터가 있습니다. 이 클러스터는 믿을 수 있는 진실한 원천 데이터베이스를 포함하고 있으며, 이는 Facebook 시스템에 있는 모든 데이터에 대한 믿을만한 복사본을 보유하고 있습니다.
이 저장소 클러스터는 데이터 일관성, 내구성 및 신뢰성을 관리합니다. 여러 리전에 걸쳐 데이터를 복제하고 1차-2차로 구성된 아키텍처를 사용함으로써 Facebook은 고가용성과 내결함성을 달성했습니다.
Facebook이 채택한 주요 철학은 백엔드에 과도한 로드를 부과하는 것보다 약간 오래된 데이터를 노출하는 것입니다.
항상 완벽한 데이터 일관성을 위해 노력하는 것이 아니라, 페이스북은 사용자들이 때때로 그들의 피드에서 구식 정보를 볼 수 있다는 것을 받아들였습니다. 이 접근 방식은 백엔드 인프라에 과도한 부담을 주지 않으면서 높은 트래픽 부하를 처리할 수 있게 해주었습니다.
이 아키텍처가 매일 수십억 건의 요청을 받는 전례 없는 규모로 작동하도록 하려면 Facebook은 다음과 같은 여러 과제를 해결해야 했습니다:
- 클러스터 내 지연 시간 및 장애 관리
- 리전 내 데이터 복제 관리
- 리전간 데이터 일관성 관리
다음 몇 섹션에서는 Facebook이 이러한 각 문제를 어떻게 처리했는지 살펴볼 것입니다.
클러스터의 도전과제
클러스터 작동에 있어서 3가지 주요한 목표가 있습니다.
- 지연 시간 줄이기
- 데이터베이스 부하 줄이기
- 실패 처리하기
지연 시간 줄이기
앞서 언급했듯이 모든 프론트엔드 클러스터에는 수백 개의 Memcache 서버가 포함되어 있으며 일관된 해싱과 같은 기술을 사용하여 이러한 서버에 항목을 분산합니다.
참고로, Constant Hashing은 노드 장애나 추가의 영향을 최소화하는 방식으로 여러 노드에 키 집합을 분산할 수 있도록 하는 기술입니다. Constant Hashing은 노드가 다운되거나 새로운 노드가 도입될 때 데이터의 완전한 재구성을 요구하지 않고 키의 작은 부분 집합만 재분배하면 됩니다.
그림은 키가 원 모양의 해시 공간에 매핑되고 노드에 원 위의 위치가 할당되는 일관된 해싱의 개념을 보여줍니다. 각 키는 시계 방향으로 가장 가까운 노드에 할당됩니다.
Facebook 정도의 규모에서, 하나의 웹 요청은 Memcached 서버로부터 데이터를 검색하기 위해 수백 개의 페치 요청을 유발할 수 있습니다. 사용자가 수많은 게시물과 댓글을 포함하는 인기 페이지를 로드하는 시나리오를 생각해 보세요.
단 한 번의 요청에도 웹 서버가 필요한 데이터를 채우기 위해 짧은 시간 내에 여러 Memcache 서버와 통신해야 할 수 있습니다.
이러한 대용량 데이터 페치는 캐시 적중 상황뿐만 아니라 캐시 미스가 있는 경우에도 발생합니다. 의미는 단일 Memcache 서버가 많은 웹 서버의 병목 현상으로 전환되어 최종 사용자의 지연 시간이 증가하고 성능이 저하될 수 있다는 것입니다.
이 문제를 해결하기 위해서 Facebook 은 다음 2가지의 방법을 사용합니다.
병렬 요청 및 배치 처리
병렬 요청 및 일괄 처리의 개념을 이해하려면 간단한 비유를 해보겠습니다.
물건이 필요할 때마다 슈퍼마켓에 간다고 상상해 보세요. 개별 품목을 여러 번 여행하는 것은 엄청나게 시간이 많이 걸리고 비효율적일 것입니다. 대신 쇼핑 여행을 계획하고 한 번의 방문으로 여러 품목을 함께 구매하는 것이 훨씬 효과적입니다.
Facebook 프론트엔드 클러스터의 데이터 검색에도 동일한 최적화 원칙이 적용됩니다.
데이터 검색의 효율성을 극대화하기 위해 Facebook은 서로 다른 데이터 항목 간의 종속성을 나타내는 DAG(Directed Acyclic Graph)를 구축합니다.
DAG는 어떤 데이터 항목을 동시에 가져올 수 있는지, 어떤 항목이 다른 항목에 의존하는지를 명확하게 이해합니다.
DAG를 분석하면 웹 서버는 데이터 페치의 최적 순서와 그룹화를 결정할 수 있습니다. 종속성 없이 병렬로 검색할 수 있는 데이터 항목을 식별하여 단일 배치 요청으로 그룹화합니다.
UDP 의 사용
Facebook은 웹 서버와 Memcache 서버 간의 네트워크 통신을 최적화하기 위해 영리한 전략을 사용했습니다.
페치 요청을 위해 페이스북은 클라이언트가 TCP 대신 UDP를 사용하도록 구성했습니다.
아시다시피 UDP는 무연결 프로토콜로 TCP보다 훨씬 빠릅니다. UDP를 사용하면 클라이언트가 네트워크 오버헤드가 적은 Memcache 서버로 페치 요청을 보낼 수 있어 요청 처리 속도가 빨라지고 지연 시간이 줄어듭니다.
그러나 UDP는 패킷의 전달을 보장하지 않는다는 단점이 있습니다. 전송 도중 패킷이 손실되면 UDP는 패킷을 재전송하는 내장된 메커니즘이 없습니다.
그런 경우를 처리하기 위해 클라이언트 측에서는 UDP 패킷 손실을 캐시 미스로 처리했습니다. 특정 시간 내에 응답이 수신되지 않으면 클라이언트는 캐시에서 데이터를 사용할 수 없다고 가정하고 기본 데이터 소스에서 데이터를 가져오도록 진행합니다.
업데이트 및 삭제 작업을 위해 페이스북은 패킷을 올바른 순서로 전달할 수 있는 신뢰할 수 있는 통신 채널을 제공했기 때문에 TCP를 여전히 사용했습니다. 업데이트 및 삭제 작업을 처리할 때 중요한 특정 재시도 메커니즘을 추가할 필요가 없었습니다.
이 모든 요청은 웹서버와 동일한 기계에서 실행되는 mcrouter라는 특수한 프록시를 거칩니다. mcrouter를 데이터 직렬화, 압축, 라우팅, 일괄 처리, 오류 처리 등 여러 가지 업무를 수행하는 중간자로 생각합니다. mcrouter에 대해서는 나중 절에서 살펴보도록 하겠습니다.
부하 줄이기
Memcache의 가장 중요한 목표는 데이터베이스에서 데이터를 가져오는 빈도를 줄여 데이터베이스의 부하를 줄이는 것입니다.
이 문제는 Memcache를 look-aside 캐시로로 사용하면 상당히 해결되지만 페이스북 규모에서는 캐싱과 관련된 두 가지 문제가 쉽게 나타날 수 있습니다.
오래된 집합: 캐시가 오래된 데이터로 설정되어 있고 캐시를 무효화할 수 있는 쉬운 방법이 없을 때 발생합니다.
Thundering Herd: 이 문제는 동시적인 환경에서, 캐시 미스가 데이터베이스에 번개떼 같은 요청들을 보낼 때 발생할 수 있습니다.
아래 다이어그램은 이 두 가지 문제를 모두 시각화한 것입니다.
페이스북은 이 두 가지 중요한 문제의 가능성을 최소화하기 위해 리스(자원에 대한 일시적 임대)라는 기술을 사용했습니다.
임대를 통해 오래된 집합과 천둥 떼 문제를 모두 해결할 수 있었고, Facebook은 최대 DB 쿼리 속도를 17K/초에서 1.3K/초로 단축할 수 있었습니다.
오래된 집합
클라이언트가 특정 키에 대해 memcache를 요청하면 캐시가 누락되는 것을 고려합니다. 이제 동일한 키에 대한 향후 요청이 캐시 누락으로 이어지지 않도록 데이터베이스에서 데이터를 가져올 뿐만 아니라 memcache를 업데이트하는 것도 클라이언트의 책임입니다.
이는 대부분의 경우 정상적으로 작동하지만 동시성이 높은 환경에서는 클라이언트가 설정하는 데이터가 캐시에서 업데이트될 때까지 오래될 수 있습니다.
리스는 이러한 일이 발생하지 않도록 합니다. 리스를 통해 Memcache는 캐시 미스가 있을 때마다 데이터를 캐시에 설정하기 위해 리스(특정 키에 바인딩된 64비트 토큰)를 특정 클라이언트에게 넘깁니다.
클라이언트는 캐시에 값을 설정할 때 이 토큰을 제공해야 하며 memcache는 토큰을 확인하여 데이터를 저장해야 하는지 확인할 수 있습니다. 클라이언트가 업데이트를 시도할 때 항목이 이미 무효화된 경우 Memcache는 리스 토큰을 무효화하고 요청을 거부합니다.
천둥떼
리스 기술은 또한 천둥떼 문제를 해결할 수 있습니다.
이 수정 사항에서 Memcache는 리스 토큰의 발행 속도를 규제합니다. 예를 들어 키당 5초에 한 번씩 토큰을 반환할 수 있습니다.
리스 토큰이 발행된 후 5초 이내에 키를 요청하면 Memcahce는 클라이언트에게 대기 및 재시도를 요청하는 특별한 응답을 보내 이러한 요청이 불필요하게 데이터베이스를 타격하지 않도록 합니다. 리스 토큰을 보유한 클라이언트가 곧 캐시를 업데이트하고 대기 중인 클라이언트가 재시도를 하면 캐시 히트를 할 가능성이 높기 때문입니다.
실패 처리
페이스북과 같은 대규모 시스템에서 실패는 피할 수 없는 현실입니다.
수백만 명의 사용자가 플랫폼을 사용하는 상황에서 Memcache에서 데이터 검색이 중단되면 심각한 결과를 초래할 수 있습니다. 클라이언트가 Memcache에서 데이터를 가져올 수 없으면 백엔드 서버에 과도한 부하가 발생하여 다운스트림 서비스에 영구적인 실패로 이어질 수 있습니다.
실패의 2 가지 경우
Facebook은 Memcache와 관련하여 크게 두 가지 수준의 실패에 직면했습니다:
소규모 운영 중단: 네트워크 문제 또는 기타 국지적인 문제로 인해 소수의 호스트에 액세스할 수 없게 될 수 있습니다. 이러한 운영 중단의 범위는 제한적이지만 전체 시스템 성능에 영향을 미칠 수 있습니다.
광범위한 운영 중단: 더 심각한 경우 전체 클러스터가 중단되어 상당한 비율의 Memcache 호스트에 영향을 미칠 수 있습니다. 이러한 광범위한 운영 중단은 시스템의 안정성과 가용성에 더 큰 위협이 됩니다.
광범위한 장애 대응
Facebook은 클러스터 다운의 영향을 완화하기 위해 웹 요청을 다른 기능 클러스터로 전환합니다.
Facebook은 부하를 재분배함으로써 문제가 있는 클러스터가 정상 상태로 복구될 수 있을 때까지 책임에서 벗어나게 합니다.
자동화된 장애 복구
소규모 운영 중단의 경우 Facebook은 영향을 받는 인스턴스를 대체할 새로운 인스턴스를 생성하여 호스트 수준의 문제를 자동으로 감지하고 대응하는 자동 복구 시스템에 의존합니다.
그러나 교정 프로세스는 즉각적이지 않으며 완료하는 데 시간이 다소 걸릴 수 있습니다. 이 시간 창 동안 백엔드 서비스는 클라이언트가 사용할 수 없는 Memcache 호스트에서 데이터를 가져오려고 시도할 때 요청이 급증할 수 있습니다.
이를 처리하는 일반적인 방법은 키를 재 해시하여 나머지 서버에 배포하는 것입니다.
그러나 페이스북의 엔지니어링 팀은 이 접근 방식이 여전히 연쇄적으로 실패하기 쉽다는 것을 깨달았습니다. 시스템에서 많은 키가 단일 서버에 대한 요청의 상당 부분( 거의 20%)을 차지할 수 있습니다. 실패 시나리오 동안 트래픽이 많은 이 키를 다른 서버로 이동하면 과부하가 발생하고 더 불안정해질 수 있습니다.
이러한 위험을 완화하기 위해 Facebook은 Gutter 기계를 사용하는 접근 방식을 취했습니다. 각 클러스터 내에서 Gutter 기계로 특별히 지정된 기계 풀(일반적으로 Memcache 서버의 1%)을 할당합니다. 이러한 기계는 운영 중단 중에 영향을 받는 Memcache 서버의 책임을 대신하도록 설계되었습니다.
작동 방식은 이렇습니다:
- Memcache 클라이언트가 응답을 받지 못하는 경우(캐시 미스도 아님), 클라이언트는 서버에 장애가 발생한 것으로 간주하고 Gutter 풀에 요청을 발행합니다.
- Gutter 풀에 대한 요청이 캐시 미스를 반환하면 클라이언트는 데이터베이스를 쿼리하고 데이터를 Gutter 풀에 삽입하여 Memcache에서 후속 요청을 제공할 수 있습니다.
- Gutter 집합은 무효화할 필요 없이 빠르게 제거됩니다.
리전 레벨에서의 도전
리전 레벨에서 처리해야 할 프론트엔드 클러스터가 여러 개 있었고 주요 과제는 모든 클러스터에서 Memcache 무효화를 처리하는 것이었습니다.
로드 밸런서에 따라 사용자가 데이터를 요청할 때 서로 다른 프론트엔드 클러스터에 연결할 수 있습니다. 그러면 특정 데이터 조각이 여러 클러스터에 캐싱됩니다.
즉, 특정 키가 지역 내 여러 클러스터의 Memcached 서버에 캐시되는 시나리오를 가질 수 있습니다. 아래 다이어그램은 이러한 시나리오를 보여줍니다:
예를 들어, “abc” 키와 “xyz” 키는 영역 내의 여러 프론트 엔드 클러스터에 존재하며, 해당 값을 업데이트할 경우 이를 무효화해야 합니다.
클러스터 레벨에서의 무효화
클러스터 수준에서 이 데이터를 무효화하는 것이 비교적 간단합니다. 데이터를 수정하는 웹 서버는 해당 클러스터의 데이터를 무효화할 책임이 있습니다. 이것은 요청을 한 사용자에게 쓰기 후 읽기 일관성을 제공합니다. 또한 클러스터 내의 오래된 데이터의 수명을 줄입니다.
참고로, 쓰기 후 읽기 일관성은 사용자가 일부 업데이트를 하면 해당 업데이트가 페이지를 다시 로드할 때 항상 표시되어야 한다는 보장입니다.
리전 레벨에서의 무효화
리전 레벨 무효화의 경우 무효화 프로세스가 조금 더 복잡하며 웹 서버가 이를 처리하지 않습니다. 대신 페이스북은 다음과 같이 작동하는 무효화 파이프라인을 만들었습니다:
- mcsqueal로 알려진 무효화 데몬은 스토리지 클러스터 내의 모든 데이터베이스 서버에서 실행됩니다.
- 이 데몬은 커밋 로그를 검사하고 삭제 내용을 추출한 후 해당 영역 내의 모든 프론트엔드 클러스터에 있는 Memcache 배포로 브로드캐스트합니다.
- 더 나은 성능을 위해 mcsqueal은 이러한 삭제를 더 적은 수의 패킷으로 배치하고 각 클러스터에서 mcrouter 인스턴스를 실행하는 전용 서버로 보냅니다.
- mcrouter 인스턴스는 배치 내의 개별 삭제를 반복하고 올바른 Memcache 서버로 라우팅합니다.
글로벌 리전에서의 도전
Facebook 규모로 운영하려면 전 세계적으로 데이터 센터를 운영하고 유지해야 합니다.
하지만 여러 지역으로 확장하면 여러 가지 문제가 발생합니다. 가장 큰 문제는 Memcache의 데이터와 여러 지역의 영구 스토리지 간의 일관성을 유지하는 것입니다.
Facebook의 지역 설정에서는 한 지역이 기본 데이터베이스를 보유하고 다른 지역은 읽기 전용 복제본을 포함합니다. 복제본은 MySQL의 복제 메커니즘을 사용하여 기본 데이터베이스와 동기화됩니다. 하지만 복제가 수반되면 복제 지연이 발생할 수밖에 없습니다. 즉, 복제 데이터베이스가 기본 데이터베이스보다 뒤처질 수 있습니다.
일관성을 위해서 고려해야 하는 2가지의 경우가 있습니다.
주요 지역으로부터의 쓰기 발생
주요 지역(미국)의 웹 서버가 사용자로부터 프로필 사진을 업데이트하라는 요청을 받았다고 가정해 보겠습니다. 일관성을 유지하려면 이 변화를 다른 지역에도 전파해야 합니다.
- 복제본 데이터베이스를 업데이트해야 합니다.
- 또한 보조 영역의 Memcache 인스턴스를 무효화해야 합니다.
까다로운 부분은 복제와 함께 무효화를 관리하는 것입니다. 실제 변경 사항이 해당 지역의 데이터베이스에 복제되기 전에 무효화가 보조 지역(유럽)에 도착하면 다음과 같은 레이스 상태가 발생할 가능성이 있습니다:
- 유럽 지역의 누군가가 프로필 사진을 보려고 합니다.
- 시스템이 캐시에서 정보를 가져오지만 유효하지 않습니다.
- 영역 내 읽기 전용 데이터베이스에서 데이터를 가져오는데, 이 데이터베이스는 여전히 지연되고 있습니다. 이는 가져오기 요청이 이전 사진을 가져와 캐시 내에서 설정한다는 것을 의미합니다.
- 결국 복제는 성공적이지만 캐시는 이미 오래된 데이터로 설정되어 있으며 이후 요청은 캐시에서 오래된 데이터를 계속 가져올 것입니다.
페이스북은 이러한 경쟁 상황을 방지하기 위해 최신 정보를 가진 스토리지 클러스터가 지역 내에서 무효화를 전송하는 솔루션을 구현했습니다. 이전 섹션에서 설명한 것과 동일한 mcsqual 설정을 사용합니다.
이 접근 방식을 사용하면 데이터베이스에서 변경사항이 완전히 복제되기 전에 무효화가 복제본 영역으로 조기에 전송되지 않습니다.
보조 지역으로부터 쓰기 발생
주요 지역이 아닌 보조 지역에서 발생한 쓰기를 처리할 때 이벤트 시퀀스는 다음과 같습니다:
- 사용자는 보조 영역에서 프로필 사진을 업데이트합니다. 읽기는 복제본 또는 보조 영역에서 제공되는 동안 쓰기는 기본 영역으로 이동합니다.
- 쓰기가 성공한 후에는 보조 영역에서도 변경 사항을 복제해야 합니다.
- 그러나 복제가 따라잡기 전에 복제본 영역에 대한 읽기 요청이 Memcache에서 오래된 데이터를 가져와 캐시할 수 있는 위험이 있습니다.
이 문제를 해결하기 위해 페이스북은 리모트 마커라는 개념을 사용했습니다.
원격 마커는 로컬 복제본의 데이터가 오래되었는지 여부를 나타내는 데 사용되며 기본 영역에서 쿼리해야 합니다.
- 클라이언트 웹 서버가 K 키에 대한 데이터 업데이트를 요청하면 복제 영역에서 해당 키에 대한 원격 마커 R을 설정합니다.
- 다음으로 기본 영역에 대한 쓰기를 수행합니다.
- 또한 키 K는 복제본 영역의 Memcache 서버에서 삭제됩니다.
- 복제본 영역에서 K에 대한 읽기 요청이 표시되지만 웹 서버에서 캐시가 누락됩니다.
- 원격 마커 R이 존재하는지 확인하고, 발견되면 쿼리가 기본 영역으로 향합니다.
이 시점에서는 캐시를 먼저 확인하고 원격 마커를 확인한 다음 기본 영역에 쿼리를 수행하기 때문에 이 접근 방식이 비효율적이라고 생각할 수 있습니다. 이 시나리오에서 Facebook은 오래된 데이터를 읽을 확률을 줄이는 대신 대기 시간을 캐시 미스와 맞바꾸기로 결정했습니다.
이하의 단일 서버 최적화 전략은 스킵합니다.
결론
페이스북의 Memcache 확장 여정은 개발자와 엔지니어들에게 훌륭한 사례 연구 역할을 합니다. 방대한 양의 데이터를 처리하고 수십억 명의 사용자에게 서비스를 제공해야 하는 전 세계적으로 분산된 소셜 네트워크를 구축할 때 발생하는 어려움을 강조합니다.
Facebook은 Memcache를 구현하고 최적화함으로써 여러 수준에서 확장성 문제를 해결하는 것의 중요성을 보여줍니다. 높은 수준의 아키텍처 결정에서 낮은 수준의 서버 최적화에 이르기까지 모든 측면이 시스템의 성능, 신뢰성 및 효율성을 보장하는 데 중요한 역할을 합니다.
이 연구에서 빼야 할 세 가지 핵심 학습 사항은 다음과 같습니다:
- 궁극적인 일관성을 수용하는 것이 성능과 가용성의 핵심입니다. 하지만 모든 결정은 절충안에 대한 정확한 이해를 바탕으로 이루어져야 합니다.
- 실패는 피할 수 없는 일이며, 실패에 대비한 시스템 설계가 매우 중요합니다.
- 최적화는 여러 수준에서 수행할 수 있습니다.
리뷰
Memcached 는 캐싱을 위해서 고려될 수 있는 좋은 옵션 중에 하나입니다. 이 아티클에서는 Facebook 의 memcached 사용전략을 설명해주고 있습니다. 가장 인상깊었던 분은 circular hash 를 이용해서 캐시를 어떤 클러스터에 저장할지 정하는 전략이었습니다. 이러한 개념은 redis cluster 와 같은 분산 시스템을 구상할 때도 유용한 전략으로, 클러스터를 구성한 노드들에게 부하를 분산할 수 있는 좋은 아이디어라고 할 수 있습니다.
또한 지역성에 대한 고려도 인상깊었습니다. Facebook 은 지구적 규모의 서비스를 하기 위해서 각 지역별로 서버를 구성하고, 이런 지역 레벨에서 캐시를 무효화하는 프로세스는 클러스터 수준보다는당연히 복잡할 것입니다. 이를 해결하기 위해서 자체적인 무효화 파이프라인을 만들어서 실행한다는 점이 재밌는 아이디어라고 생각했습니다.
일전에, 페이스북 캐싱 전략을 찾아보다가 youtube 에서 영상을 본 적이 있습니다. 화질이 좋지는 않지만, 내용을 이해하기는 좋았습니다. 이번 아티클과 유사한 내용을 담고 있는데, 관심 있으시면 보시기를 추천드리며 마치겠습니다.