카카오

카카오톡, 다음, 카카오맵, 멜론

개발팀 리뷰

위 내용은 카카오 전 • 현 재직자의 응답 결과입니다.

기술 스택

언어

javascript

Java

Kotlin

프론트엔드

Angular

ReactJS

Redux

백엔드

NestJs

Spring

SpringBoot

데이터베이스

PostgreSQL

Redis

mongodb

데브옵스

Argo CD

Jenkins

OpenEBS

재직자가 작성한 글

재직자가 좋아한 글

Facebook 이 캐시 데이터 일관성을 관리하는 방법  |  최근에 작업이 마무리되서 오랜만에 글 쓰네요. 이번 글은 시스템 설계에 대해 공부하다가 관련 좋은 논문을 봐서 가져왔습니다. 글의 주제는 "Facebook 에서 캐시 데이터의 일관성을 관리하는 방법' 이에요. Facebook 의 시스템은 초당 수십억 개의 요청이 들어온다고 합니다. 이런 대규모 트래픽 상황에서는 일관성을 유지하는 것은 적은 트래픽일 때보다 훨씬 복잡할건데요. 본문에서는 Facebook이 이런 과도한 트래픽 상황에서 어떻게 캐시 데이터와 영속성 데이터의 일관성을 유지하는지 소개해볼게요. 본 글은 Facebook 에서 발표한 논문인 Scaling Memcache at Facebook 를 참고해서 많이 요약한 글입니다. 해당 논문에서는 캐시 서버를 페이스북 트래픽에 맞게 확장하는 방법도 상세히 소개하니, 궁금하시면 원문을 확인해 보는 걸 추천드립니다. 링크는 아래에 적어둘게요. <1. High-Level Overview of Facebook's System> 시스템 구조는 그림으로 봐도 좋을 것 같은데요. 아래의 설명과 첨부한 이미지를 비교해보면서 보면 더 이해하기 쉬울 것 같습니다. 먼저, Facebook은 전 세계에 걸쳐 클러스터를 여러 지역(Region)으로 분산하여 관리하고 있구요, 각 지역은 다음과 같은 구성 요소로 이루어져 있습니다: * Front-End Clusters (웹 서버 + Memcached): 사용자의 요청을 처리하고 데이터를 캐싱하여 빠른 응답 시간을 보장하는 역할을 합니다. * Storage Cluster (MySQL 등): 실제 사용자 데이터와 시스템 정보를 저장하는 역할을 합니다.   Storage Cluster와 Front-End Cluster의 상호 작용: * Storage Cluster 에서 생성된 데이터는 Front-End Cluster 내의 Memcached 로 복제되어 사용자에게 빠른 데이터 접근을 제공하구요. 또한, Storage Cluster 는 캐시 무효화 요청을 통해 기본적으로 Memcached 에 최신 데이터를 유지하려고 합니다.  다중 Region 과 데이터베이스 구성: * Facebook은 다수의 Region 을 사용하며, 각 Region 마다 데이터베이스 Master 와 Slave 로 구분합니다. Front-End Clusters 의 다중화: * 하나의 지역 내에서도 Front-End Clusters 는 여러 개로 분리됩니다. 이렇게 분리된 이유는 다음과 같습니다: * Network Incast Congestion: 네트워크 내 많은 호스트가 동시에 소수의 수신자(예: Memcached)에 데이터를 전송할 때 발생하는 네트워크 혼잡 현상입니다. 이 현상은 네트워크 대역폭 병목 현상으로 이어져 패킷 손실과 지연 시간 증가의 문제를 일으킬 수 있습니다. * Fault Tolerance: 여러 개의 Cluster 로 분리함으로써, 하나의 Cluster 가 실패하더라도 다른 Cluster 들은 안전하게 운영될 수 있습니다. 캐시 스토어로 Memcached 선택: * 저지연 및 고성능 (Low Latency, High Performance) 과 제한된 엔지니어링 자원 및 시간 (Limited Engineering Resources and Time) 을 고려했을 때 Memcached 를 선택헀습니다. (개발팀에 익숙한 기술을 이유로 선택한 것 같습니다.)   <2. How Facebook Manages Cached Data> Facebook에서는 Memcached에 두 가지 주요 유형의 데이터를 저장합니다: * Query Cache: 이는 주로 데이터베이스에서 직접 조회할 수 있는 데이터로 구성됩니다. 이 데이터 캐싱 목적은 데이터베이스의 부하를 분산시키는 것으로, 데이터베이스에 대한 직접적인 쿼리 수를 줄이기 위해 캐시에 저장합니다.  * Generic Cache: 이 데이터는 머신러닝과 같은 애플리케이션에서 사전 계산된(pre-computed) 결과들로 구성됩니다. 이러한 종류의 데이터는 사용자의 시청 이력과 선호도와 같이 다양한 애플리케이션에서 활용될 수 있는 데이터들을 말합니다. Facebook은 데이터를 처리하는 두 가지 주요 경로, 즉 읽기 경로와 쓰기 경로에서 Memcached를 활용합니다: * Read Path: 'Look Aside' 패턴을 사용합니다. 즉 먼저 캐시에 데이터를 조회해보고, 캐시에 해당 데이터가 없을 경우엔 데이터베이스에서 조회한 후, 이를 캐시에 저장(Set)하는 방법입니다. * Write Path: 데이터베이스에 데이터를 쓴 후, 캐시에 해당 데이터를 무효화합니다. 데이터를 직접 업데이트하는 것도 가능하지만, 캐시 무효화 작업은 데이터의 일관성을 보장하는 멱등성 있는 연산이기 때문에 이걸 사용합니다.  총 2가지의 캐시 무효화 과정: * 1) 웹 서버는 데이터를 Storage Cluster 에 있는 영속성 저장소에 업데이트된 후, 자신의 로컬 캐시인 Memcached 에 캐시 무효화 요청을 합니다. 이 과정은 완벽한 일관성을 보장하지는 않지만, 빠른 갱신과 기본적인 일관성은 보장할 수 있습니다. * 2) 'Invalidation Batch Processing' 을 통해 클러스터 전체에 캐시 무효화 요청을 전달합니다. 이는 데이터베이스에서 커밋된 로그를 바탕으로 모든 변경 사항을 감지하고, 해당 지역의 모든 Front-End Cluster 에 있는 Memcached 서버에 무효화 요청을 보내는 과정입니다. 중요한 건 이 요청들은 직접적으로 Memcached 로 요청이 전달되진 않습니다. 중간 라우터 계층인 mcrouter 를 통해 캐시 무효화 과정은 Memcached 로 전달됩니다. mcrouter의 역할: * mcrouter는 이러한 캐시 무효화 요청을 관리하는 컴포넌트입니다. 배치 프로세스에서 Memcached 로 직접 캐시 무효화 요청을 전송하면 해당 시스템에 과부하가 발생할 수 있어, mcrouter를 사용하여 이를 방지하는 목적으로 사용합니다. * mcrouter 인스턴스는 배치 요청을 받아 적절한 Memcached 서버로 라우팅하는 역할을 합니다. 또한, Memcached 가 응답하지 않을 경우를 대비해 캐시 무효화 요청을 내부적으로 버퍼링하는 기능을 갖추고 있습니다. <3. How Facebook Ensures Cache Data Consistency> Facebook Design Goals: * Facebook의 설계 목표는 대규모 트래픽을 효과적으로 처리하는 것입니다. 이를 위해 일관성(Consistency)만을 고려할 수는 없으며, 일관성과 성능(Performance) 사이의 균형을 찾는 것이 중요합니다. 그래서 Facebook 은  Strong Consistency 를 보장하는 대신 Eventually Consistency를 채택합니다.  근본적인 일관성 문제의 원인: * 캐시 데이터와 영속성 저장소 사이의 일관성 문제는 주로 데이터 복제 지연 때문에 발생합니다. 만약 복제 지연이 없다면 캐시 무효화를 통해 일관성을 유지할 수 있습니다. 하지만 복제 지연이 발생하는 상황에서 슬레이브 데이터베이스에서 오래된 데이터를 검색할 경우, 캐시 무효화 이후에도 캐시에 오래된 데이터가 설정될 위험이 있습니다. Facebook 에서의 일관성 보장 전략: * 결론부터 말하자면 복제 지연이 생겼다고 판단되는 경우에 데이터 조회를 Slave 에서 하지 않고 Master 에서 할 수 있도록 라우팅 전략을 수립하는 것입니다.   일관성을 보장하는 매커니즘: * 1) 원격 마커 설정: 데이터 업데이트를 원하는 웹 서버는 먼저 해당 리전에 대한 원격 마커 rk 를 설정합니다. 이 마커는 Slave 데이터베이스의 데이터가 오래된 것 일 수 있음을 나타냅니다. * 2) Master 데이터베이스에 쓰기: 서버는 마스터 데이터베이스에 쓰기를 수행하면서, 해당 쓰기 작업이 무효화되어야 할 키 k 와 원격 마커 rk 를 SQL 문에 포함시킵니다. 이는 마스터에서의 변경 사항이 복제될 때까지 rk 를 유지하게 만들어, 복제가 완료된 후에만 rk를 무효화하도록 만드는 과정입니다.  * 3) 로컬 클러스터에서의 키 삭제: 서버는 로컬 클러스터에서 키 k를 삭제합니다. 이렇게 하면 후속 요청이 발생했을 때, 로컬 캐시에서 k 를 찾을 수 없게 되고, 웹 서버는 rk 의 존재 여부를 확인합니다.  * 4) 쿼리 리디렉션:  웹 서버는 rk의 존재 여부를 확인하고, rk가 존재한다면 쿼리를 마스터 리전으로, 그렇지 않다면 로컬 리전으로 리디렉션합니다. rk 를 삭제하는 타이밍은? * 복제 작업이 완료될 때, 이전에 본 배치 프로세스에서 데이터베이스 로그를 보고 삭제해야 할 rk 를 판단해서, 캐시 무효화 요청을 전달하는 작업을 통해 삭제됩니다. 이 방법의 한계점: * rk 는 동시성 상황에서 Race Condition 으로 인해 여전히 존재할 수 있습니다. 그러나 Facebook은 이러한 상황이 매우 드물기 때문에 문제로 보지는 않습니다. * 복제 지연 상황에서는 마스터 데이터베이스로의 질의가 증가하는 것을 피할 수 없습니다. 운영 고려사항: * 복제를 위해 리전 간 통신은 비용이 많이 들고 네트워크 지연(latency)을 증가시키므로, 데이터베이스 복제와 삭제 스트림을 위한 전용 통신 채널을 구축하여 네트워크 효율성을 향상시킵니다. * Memcached가 응답하지 않을 때를 대비해야합니다. Facebook 은 캐시 무효화 요청을 버퍼링할 수 있도록 만들고, Memcached 가 다시 작동하게 되면 캐시 무효화 요청을 재생할 수 있도록 구축했습니다.   https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf

좋아요 33 저장 59