apache-druid

3 개의 포스트

똑같은 질문에 두 번 답하지 마세요: 넷플릭스 규모의 Druid를 위한 인터벌 인식 캐싱 (새 탭에서 열림)

넷플릭스는 Apache Druid를 통해 초당 1,500만 건 이상의 이벤트를 처리하며 대규모 실시간 분석을 수행하고 있으나, 대시보드의 롤링 윈도우(Rolling Window) 쿼리가 생성하는 중복 부하 문제를 해결해야 했습니다. 이를 위해 쿼리에서 시간 범위를 분리하여 처리하는 '구간 인식 캐싱(Interval-Aware Caching)' 레이어를 구축하여 Druid의 계산 리소스를 효율화했습니다. 이 시스템은 과거의 안정된 데이터는 캐시에서 불러오고 오직 최신 데이터만 Druid에 요청함으로써, 대규모 트래픽 상황에서도 쿼리 성능을 안정적으로 유지합니다. ### 기존 캐싱 방식의 한계와 문제점 * **롤링 윈도우의 비효율성**: 실시간 모니터링 대시보드는 10~30초마다 "최근 3시간"과 같은 쿼리를 반복해서 보냅니다. 시간 범위가 계속 이동하기 때문에 Druid의 기존 전체 결과 캐시(Full-result cache)는 매번 미스(Miss)가 발생합니다. * **실시간 데이터 캐싱 제한**: Druid는 데이터의 정확성을 위해 실시간 인덱싱 중인 세그먼트의 결과는 캐싱하지 않습니다. 이로 인해 대시보드가 갱신될 때마다 동일한 실시간 세그먼트를 반복해서 스캔하는 낭비가 발생합니다. * **하드웨어 확장의 한계**: 수십 명의 엔지니어가 동일한 대시보드를 볼 때 발생하는 수천 개의 중복 쿼리를 처리하기 위해 단순히 하드웨어를 증설하는 것은 비용 효율성이 매우 낮습니다. ### 구간 인식 캐싱의 핵심 아이디어 * **데이터의 안정성 활용**: 3시간 전의 데이터는 이미 확정되어 변하지 않지만, 최근 1분 내의 데이터는 지연 도착 등으로 인해 변할 수 있습니다. 이 차이를 이용해 오래된 데이터는 캐시에서 즉시 반환하고, 최신 구간만 Druid에 쿼리합니다. * **쿼리 구조와 시간의 분리**: 쿼리문에서 시간 범위(Interval)를 제외한 나머지 구조(필터, 집계 등)를 SHA-256으로 해싱하여 캐시 키로 사용합니다. 이를 통해 서로 다른 시간 범위를 가진 동일한 목적의 쿼리들이 동일한 캐시 항목을 참조할 수 있게 합니다. * **버킷팅(Bucketing) 구조**: 데이터를 쿼리 단위(예: 1분)별로 잘게 쪼개어 'Map-of-Maps' 형태로 저장합니다. 쿼리가 들어오면 필요한 시간 범위에 해당하는 버킷들을 캐시에서 조회하고, 없는 부분만 골라냅니다. ### 지수적 TTL을 통한 효율적인 데이터 관리 * **신선도와 부하의 트레이드오프**: 데이터 파이프라인의 지연 시간을 고려해 최신 데이터에 약 5초의 캐시 유지 시간(TTL)을 부여합니다. 이는 대시보드 사용자에게는 거의 실시간으로 느껴지면서도 Druid의 부하를 대폭 줄여줍니다. * **데이터 연령에 따른 TTL 차등화**: 데이터가 생성된 지 얼마 안 된 버킷은 5~10초의 짧은 TTL을 가집니다. 데이터가 오래될수록 나중에 도착하는 이벤트가 적어지므로, TTL을 지수적으로 늘려 최대 1시간까지 캐시에 보관합니다. * **자동 보정**: 짧은 TTL 덕분에 최신 데이터 구간에서 발생하는 수정 사항은 빠르게 캐시에 반영되며, 오래된 구간은 긴 TTL을 통해 캐시 적중률을 극대화합니다. ### 시스템 구현 및 작동 워크플로우 * **투명한 프록시 구조**: Druid Router 단계에서 요청을 가로채는 외부 서비스 형태로 구현되었습니다. 클라이언트 앱을 수정할 필요 없이 캐싱 기능을 끄거나 켤 수 있습니다. * **쿼리 분해 및 병합**: 1. 들어온 쿼리를 파싱하여 시간 구간을 확인하고 캐시 키(해시)를 생성합니다. 2. 캐시 저장소(예: Redis/Memcached)에서 요청된 구간에 해당하는 연속된 버킷들을 확인합니다. 3. 캐시에 없는 '가장 최신의 불안정한 구간'으로 쿼리 범위를 축소하여 Druid에 요청합니다. 4. 캐시된 결과와 Druid에서 새로 가져온 결과를 병합하여 클라이언트에 반환합니다. 롤링 윈도우 기반의 대규모 대시보드를 운영하는 환경이라면, 모든 데이터를 매번 다시 계산하기보다 이처럼 시간 구간을 나누어 캐싱하는 전략이 Druid 클러스터의 비용 절감과 성능 향상에 매우 효과적입니다. 특히 데이터가 확정되는 속도에 따라 TTL을 다르게 가져가는 '지수적 TTL' 방식은 데이터 정확도와 효율성 사이의 균형을 잡는 유용한 기술적 패턴입니다.

고객은 절대 기다려주지 않는다: 빠른 데이터 서빙으로 고객 만족도를 수직 상승 시키는 법 (새 탭에서 열림)

토스페이먼츠는 가파른 성장세에 따른 데이터 조회 부하를 해결하기 위해 CQRS 아키텍처를 도입하고 Apache Druid를 중심으로 한 데이터 서빙 환경을 구축했습니다. 초기에는 Elasticsearch와 Druid를 결합하여 대규모 시계열 데이터의 실시간 집계와 검색 성능을 확보했으며, 이를 통해 비용 효율성과 시스템 안정성을 동시에 달성했습니다. 현재는 Druid의 조인 제약과 멱등성 문제를 해결하기 위해 StarRocks를 도입하며, 도메인 간 결합이 자유로운 통합 원장 시스템으로 진화하고 있습니다. ### CQRS와 Apache Druid 도입 배경 * **MSA 전환과 DB 분리:** 서비스 규모가 커지며 모놀리식에서 MSA로 전환했으나, DB가 분산되면서 도메인 간 조인이나 통합 조회가 어려워지는 문제가 발생했습니다. * **명령과 조회의 분리:** 읽기 전용 저장소로 Apache Druid를 선택하여 원장 DB(MySQL)의 부하를 줄이고, 수십억 건의 데이터를 저지연으로 조회하는 CQRS 구조를 설계했습니다. * **Druid의 기술적 이점:** 시계열 데이터 최적화, SQL 지원을 통한 낮은 러닝 커브, 모든 컬럼의 비트맵 인덱스(Bitmap Index)화, 그리고 클라우드 네이티브 구조를 통한 비용 효율성을 고려했습니다. ### 데이터 가공 및 메시지 발행 방식 * **CDC 대신 메시지 발행 선택:** 데이터팀이 도메인 로직을 직접 소유해야 하는 CDC 방식 대신, 각 도메인 팀에서 완성된 데이터를 발행하는 방식을 채택하여 시스템 의존성을 Kafka로 단순화했습니다. * **역정규화 테이블 구성:** 복잡한 수단별 원장 데이터를 조회 친화적인 역정규화 테이블로 변환하여 적재했으며, JSON 필드 단위까지 비트맵 인덱스가 생성되어 효율적인 질의가 가능해졌습니다. ### AWS 환경에서의 비용 및 성능 최적화 * **컴퓨팅과 스토리지 분리:** 고가의 네트워크 스토리지(EBS) 대신 S3를 영구 저장소로 활용하고, 쿼리 수행 시에는 로컬 SSD를 사용하여 성능을 9배 이상 향상했습니다. * **스팟 인스턴스 활용:** 데이터가 S3에 안전하게 보관되는 특성을 이용해 개발/테스트 환경에서 스팟 인스턴스를 적극적으로 사용하여 월 5,000만 원 이상의 클라우드 비용을 절감했습니다. * **고가용성 확보:** 네트워크 스토리지 의존성을 제거함으로써 가용 영역(AZ) 간 분산 배치가 유연해져 시스템의 안정성을 높였습니다. ### Druid 운영의 기술적 도전과 극복 * **파편화 및 멱등성 문제:** 데이터가 시점별로 분산되는 파편화 현상을 해결하기 위해 60초 주기 탐지 프로세스와 자동 컴팩션(Compaction)을 도입했습니다. * **Rollup을 통한 성능 극대화:** 동일 차원의 데이터를 자동 집계하여 저장하는 Rollup 기능을 적용해, 수십 초 걸리던 집계 쿼리 응답 속도를 0.5~1초 내외로 99% 이상 개선했습니다. * **ES 하이브리드 아키텍처:** 단일 ID 기반의 고속 검색은 Elasticsearch가 담당하고, 필터링된 결과의 대규모 집계는 Druid가 처리하도록 역할을 분담해 검색 성능을 안정화했습니다. ### StarRocks 도입을 통한 통합 원장 구축 * **조인 및 멱등성 한계 극복:** Druid의 제한적인 조인 기능과 멱등성 처리의 어려움을 해결하기 위해 StarRocks를 새롭게 도입했습니다. * **도메인 간 데이터 결합:** 결제부터 매입, 정산까지 이르는 전체 라이프사이클을 한눈에 볼 수 있는 통합 원장을 구현하여 비즈니스 요구사항에 유연하게 대응하고 있습니다. **결론적으로** 대규모 트래픽 환경에서는 단순한 DB 분리를 넘어 검색(ES), 시계열 집계(Druid), 그리고 복잡한 조인과 멱등성 보장(StarRocks)이라는 각 도구의 장점을 살린 하이브리드 아키텍처 설계가 필수적입니다. 특히 스토리지와 컴퓨팅을 분리한 구조는 비용 절감뿐만 아니라 운영의 유연성을 확보하는 핵심 전략이 됩니다.

스케일링 뮤즈: 넷플릭스가 조단위 데이터에서 데이터 기반 창의적 인사이트를 제공하는 방법 | 넷플릭스 기술 블로그 | 넷플릭스 기술 블로그 (새 탭에서 열림)

넷플릭스의 내부 데이터 분석 플랫폼인 'Muse'는 수조 건 규모의 데이터를 분석하여 홍보용 미디어(아트웍, 영상 클립)의 효과를 측정하고 창작 전략을 지원합니다. 급증하는 데이터 규모와 복잡한 다대다(Many-to-Many) 필터링 요구사항을 해결하기 위해, 넷플릭스는 HyperLogLog(HLL) 스케치와 인메모리 기술인 Hollow를 도입하여 데이터 서빙 레이어를 혁신했습니다. 이를 통해 데이터 정확도를 유지하면서도 수조 행의 데이터를 실시간에 가깝게 처리할 수 있는 고성능 OLAP 환경을 구축했습니다. ### 효율적인 고유 사용자 집계를 위한 HLL 스케치 도입 * **근사치 계산을 통한 성능 최적화:** 고유 사용자 수(Distinct Count)를 계산할 때 발생하는 막대한 리소스 소모를 줄이기 위해 Apache Datasketches의 HLL 기술을 도입했습니다. 약 0.8%~2%의 미세한 오차를 허용하는 대신 집계 속도를 비약적으로 높였습니다. * **단계별 스케치 생성:** Druid 데이터 수집 단계에서 '롤업(Rollup)' 기능을 사용해 데이터를 사전 요약하고, Spark ETL 과정에서는 매일 생성되는 HLL 스케치를 기존 데이터와 병합(hll_union)하여 전체 기간의 통계를 관리합니다. * **데이터 규모 축소:** 수개월에서 수년 치의 데이터를 전수 비교하는 대신, 미리 생성된 스케치만 결합하면 되므로 데이터 처리량과 저장 공간을 획기적으로 절감했습니다. ### Hollow를 활용한 인메모리 사전 집계 및 서빙 * **초저지연 조회 구현:** 모든 쿼리를 Druid에서 처리하는 대신, 자주 사용되는 '전체 기간(All-time)' 집계 데이터는 넷플릭스의 오픈소스 기술인 'Hollow'를 통해 인메모리 방식으로 서빙합니다. * **Spark와 마이크로서비스의 연계:** Spark 작업에서 미리 계산된 HLL 스케치 집계 데이터를 Hollow 데이터셋으로 발행하면, Spring Boot 기반의 마이크로서비스가 이를 메모리에 로드하여 밀리초(ms) 단위의 응답 속도를 제공합니다. * **조인(Join) 병목 해결:** 복잡한 시청자 성향(Audience Affinity) 필터링과 같은 다대다 관계 연산을 메모리 내에서 처리함으로써 기존 아키텍처의 한계를 극복했습니다. ### 데이터 검증 및 아키텍처 현대화 * **신뢰성 보장:** 아키텍처 변경 전후의 데이터 정합성을 확인하기 위해 내부 디버깅 도구를 활용하여 사전/사후 데이터를 정밀하게 비교 검증했습니다. * **기술 스택 고도화:** React 프런트엔드와 GraphQL 레이어, 그리고 gRPC 기반의 Spring Boot 마이크로서비스 구조를 통해 확장성 있는 시스템을 구축했습니다. * **분석 역량 강화:** 이를 통해 단순한 대시보드를 넘어 이상치 감지(Outlier Detection), 미디어 간 성과 비교, 고급 필터링 등 사용자들의 고도화된 분석 요구를 수용할 수 있게 되었습니다. 대규모 OLAP 시스템을 설계할 때 모든 데이터를 실시간으로 전수 계산하기보다는, HLL과 같은 확률적 자료구조와 Hollow 기반의 인메모리 캐싱을 적절히 조합하는 것이 성능 최적화의 핵심입니다. 특히 수조 건 규모의 데이터에서는 완벽한 정확도와 성능 사이의 트레이드오프를 전략적으로 선택하는 것이 시스템의 유연성을 결정짓습니다.