query-engine

2 개의 포스트

Husky 쿼리 엔진 내부: 100조 개 이벤트에 대한 실시간 액세스 (새 탭에서 열림)

Datadog은 매일 100조 개 이상의 이벤트와 수십억 개의 쿼리를 처리하기 위해 3세대 이벤트 저장소인 'Husky'를 구축했습니다. Husky의 쿼리 엔진은 고정된 스키마가 없고 데이터 볼륨이 가변적인 대규모 멀티테넌트 환경에서도 오브젝트 스토리지에 저장된 페타바이트급 데이터를 실시간으로 조회할 수 있도록 설계되었습니다. 이를 위해 시스템은 쿼리 플래너, 오케스트레이터, 메타데이터 서비스, 리더 서비스로 역할을 분산하여 성능과 비용 효율성을 동시에 달성했습니다. ### Husky의 데이터 모델과 주요 쿼리 패턴 Husky는 로그나 네트워크 트래픽과 같이 타임스탬프와 다양한 속성을 가진 '이벤트' 데이터를 저장하며, 서비스별로 상이한 데이터 형태를 유연하게 수용합니다. * **가변적인 스키마 지원:** 테넌트나 사용 사례(로그 vs 네트워크 데이터)에 따라 속성의 종류와 데이터의 크기가 극명하게 달성하더라도 효율적으로 처리할 수 있습니다. * **Needle-in-a-haystack 검색:** 수많은 데이터 중 특정 IP나 에러 메시지, 트레이스 ID 등을 찾아내는 고도로 선택적인 필터링 쿼리를 지원합니다. * **분석형(Analytics-style) 검색:** 특정 기간 동안의 서비스 지연 시간 추이나 지역별 매출 분석과 같이 대규모 데이터를 집계하여 시각화하는 쿼리를 최적화합니다. ### 쿼리 실행의 4단계 아키텍처 Husky의 쿼리 경로는 네 가지 핵심 서비스로 나뉘어 멀티테넌트 환경에서 안정적으로 동작합니다. * **쿼리 플래너(Query Planner):** 모든 쿼리의 진입점으로, 쿼리 유효성 검사 및 최적화를 수행합니다. 통계 데이터를 기반으로 쿼리를 시간 단위의 여러 단계로 분할하고 실행 결과를 최종 병합합니다. * **쿼리 오케스트레이터(Query Orchestrator):** 데이터 저장소의 관문 역할을 하며 메타데이터 조회, 프래그먼트(데이터 파일) 할당, 집계 조율을 담당합니다. 특히 '존 맵(Zone-map) 프루닝'을 통해 불필요한 데이터를 걸러냄으로써 다운스트림 작업량을 평균 30~60% 절감합니다. * **메타데이터 서비스(Metadata Service):** FoundationDB의 프런트엔드로서 데이터 컴팩션 중에도 쿼리 결과의 원자성(Atomicity)을 보장합니다. DB 내부 로직을 추상화하여 전체 쿼리 경로와 분리하는 역할을 합니다. * **리더 서비스(Reader Service):** 실제 오브젝트 스토리지에 저장된 프래그먼트에서 데이터를 읽어 응답을 반환하는 핵심 실행 엔진입니다. ### 리더(Reader) 서비스의 데이터 스캔 최적화 오브젝트 스토리지는 비용이 저렴하지만 읽기 속도가 느리므로, 리더 서비스는 "읽지 않아도 되는 데이터를 스캔하지 않는 것"을 최우선 목표로 삼습니다. * **행 그룹(Row Groups) 구조:** 수백만 행을 가진 대용량 프래그먼트를 '행 그룹' 단위로 물리적으로 배치하여 관리합니다. 이는 쿼리 실행 시 전체 파일을 메모리에 올리는 부담을 줄이고 메모리 부족(OOM) 오류를 방지합니다. * **입출력(I/O) 최소화:** 오브젝트 스토리지에 대한 GET 요청은 비용이 많이 들기 때문에, 쿼리에 꼭 필요한 행 그룹만 선택적으로 가져와 비용 효율성과 응답 속도를 극대화합니다. * **반복자 기반 실행 모델:** Volcano 모델에서 영감을 받은 반복자(Iterator) 방식을 사용하여 데이터를 효율적으로 스트리밍하며 처리합니다. Husky의 사례는 대규모 시계열 이벤트를 처리할 때 고정된 인덱스에 의존하기보다, 메타데이터 기반의 프루닝과 물리적인 데이터 레이아웃 최적화를 통해 오브젝트 스토리지의 한계를 극복할 수 있음을 보여줍니다. 저비용 고성능의 로그 분석 시스템을 설계한다면 데이터의 물리적 구조화와 단계별 쿼리 분산 처리가 핵심이 될 것입니다.

Introducing Husky, Datadog's third-generation event store (새 탭에서 열림)

데이터독(Datadog)은 폭발적인 데이터 증가와 다양한 제품군의 요구사항을 충족하기 위해 새로운 이벤트 저장 시스템인 '허스키(Husky)'를 구축했습니다. 기존 시스템은 대규모 멀티테넌트 환경에서 발생하는 성능 간섭과 고비용 구조, 데이터 삭제의 어려움이라는 한계에 봉착했으며, 이를 해결하기 위해 저장소와 클러스터링을 분리하고 효율성을 극대화한 새로운 아키텍처가 필요했습니다. 결과적으로 허스키는 로그, RUM, 보안 데이터 등 다양한 고카디널리티(High-cardinality) 이벤트를 경제적이고 안정적으로 처리할 수 있는 기반이 되었습니다. ### 메트릭과 로그의 기술적 차이 * **메트릭의 효율성:** 메트릭 시스템은 데이터를 `<timeseries_id, timestamp, float64>` 형태의 튜플로 사전 집계하여 저장합니다. '델타-오브-델타(delta-of-delta)' 인코딩을 통해 16바이트 데이터를 2바이트 미만으로 압축할 수 있어 매우 효율적이지만, 태그 카디널리티에 제한이 있습니다. * **로그의 복잡성:** 로그는 이벤트당 킬로바이트(KB) 단위의 크기를 가지며, UUID나 스택 트래적(stack traces)과 같은 고카디널리티 데이터를 포함해야 합니다. 로그 시스템은 모든 문맥(context)을 보존하면서 쿼리 시점에 임의의 차원으로 집계할 수 있는 능력이 필수적입니다. ### 초기 아키텍처의 한계와 클러스터링 개선 * **초기 버전의 문제:** 멀티테넌트 클러스터 내에서 단일 노드의 장애나 특정 테넌트의 과부하가 전체 클러스터의 가용성을 떨어뜨리는 '노이즈 네이버(Noisy Neighbor)' 문제가 빈번했습니다. * **저장소와 클러스터링 분리:** 두 번째 버전에서는 저장 엔진과 클러스터링 로직을 분리했습니다. '샤드 라우터(Shard Router)'가 카프카(Kafka)를 통해 데이터를 샤드 단위로 정리하고, 각 노드는 독립적인 유닛으로 동작하게 하여 장애 전파를 차단했습니다. * **커스텀 쿼리 엔진:** 여러 샤드에 분산된 데이터를 쿼리하고 부분 집계 결과를 병합하는 전용 엔진을 도입하여 신뢰성을 높였습니다. ### 플랫폼 급성장과 새로운 요구사항의 등장 * **제품군의 확장:** 로그뿐만 아니라 네트워크 성능 모니터링(NPM), 실제 사용자 모니터링(RUM), 프로파일러 등 다양한 제품이 출시되면서 저장해야 할 이벤트의 양과 종류가 급증했습니다. * **장기 보관 비용 문제:** 가끔 쿼리하지만 장기간 보관이 필요한 데이터를 기존 아키텍처에서 운영하는 것은 비용 효율성이 낮았습니다. * **데이터 관리 편의성:** GDPR 대응을 위한 특정 데이터의 정밀한 삭제 기능과 테넌트 간의 완전한 자원 격리에 대한 요구가 강해졌습니다. ### 허스키(Husky)의 설계 방향 * **범용 이벤트 저장소:** 로그와 유사한 구조를 가진 모든 유형의 데이터를 수용할 수 있는 유연한 스키마 구조를 지향합니다. * **비용 효율적인 확장:** 스토리지 계층화를 통해 자주 사용되지 않는 데이터는 저렴한 저장소에 보관하면서도 즉시 쿼리가 가능한 구조를 구축했습니다. * **격리 및 제어권 강화:** 특정 테넌트의 트래픽 급증이 다른 테넌트의 쿼리 성능에 영향을 주지 않도록 정교한 할당량(Quota) 관리와 격리 메커니즘을 포함했습니다. 시스템을 설계할 때 단순히 현재의 성능 개선에만 집중하는 것이 아니라, 향후 데이터의 기하급수적인 증가와 다양한 제품 요구사항(비용, 삭제, 격리)을 수용할 수 있는 아키텍처 유연성을 확보하는 것이 중요합니다. 특히 대규모 멀티테넌트 환경에서는 '폭포수 장애'를 방지하기 위해 각 구성 요소를 최대한 독립적으로 격리하는 설계가 필수적입니다.