Druid Overview and Architecture

gorany·2025년 1월 1일

1 Druid Hystory

사용자가 데이터를 실시간으로 탐색하고 시각화할 수 있는 시스템을 구현하고자 했음. 이 시스템은 다음과 같은 요구사항이 있었습니다.

데이터 탐색 입장에서의 요구사항

  • 어떠한 차원 조합에 대한 쿼리라도 즉각적으로 결과를 반환
  • 데이터를 자유롭게 슬라이스하고 다이스하며, 제약 없이 드릴다운할 수 있어야 함
  • 이를 통해 사용자는 데이터 대시보드에서 이벤트 스트림을 인터랙티브하게 탐색할 수 있음

데이터 수집 입장에서의 요구사항

  • 이벤트가 발생하는 즉시 데이터를 수집하고 쿼리가 가능하도록 인덱싱하는 기능이 필요. (당시의 유명한 데이터 웨어하우스 시스템, 예를 들어 Hadoop 등은 이러한 수준의 데이터 수집 및 처리 속도를 제공하지 못함 )

시스템상에서의 요구사항

  • 시스템은 멀티테넌시와 높은 가용성을 보장해야 했음.
  • 이는 많은 사용자가 동시에 접속해도 안정적으로 작동하며, 다운타임 없이 장애를 견딜 수 있는 시스템을 의미

이러한 필요를 충족하기 위해, Druid라는 데이터 엔진이 탄생. Druid는 대규모 트랜잭션 이벤트(로그 데이터)를 실시간으로 수집, 인덱싱, 분석하고, 이를 기반으로 빠르고 신뢰성 높은 쿼리 응답을 제공함.

2 Apache Druid 소개

  • 아파치 드루이드는 기존의 데이터 처리 및 쿼리에 대한 관념을 180도 바꿔주는 OLAP 데이터베이스
  • OLAP란 Online Analytical Processing의 약자로써 사용자가 적재한 데이터를 다양한 방식(다차원)으로 적재하고 분석하도록 도와주는 시스템

아파치 드루이드는 이러한 다차원 데이터를 빠른 쿼리속도를 제공하기 위해 세그먼트 단위로 데이터를 인덱스하여 저장하고. 이렇게 세그먼트를 만드는 것은 지정한 시간(time interval) 단위 이고 쿼리를 수행할 때가 아니라 데이터를 적재할때 함께 인덱싱.

  • 전통적인 데이터베이스 : 데이터 적재 → 쿼리 → 프로세싱 → 결과 확인
  • 아파치 드루이드 : 데이터 적재 → 프로세싱(인덱싱) → 쿼리 → 결과 확인

이러한 특징으로 인해서 드루이드는 전통적인 데이터베이스들에 비해서 쿼리 시간이 엄청나게 빠름. (예를 들어 전통적인 데이터베이스에 1억건의 데이터가 있다고 가정할 때 sum()에 대한 결과를 가져오는데 O(n)의 시간 복잡도가 걸림 반면에 드루이드 경우에는 미리 디멘젼(Dimension)을 지정해놓으면 데이터를 넣을 때 마다 인덱싱이 일어나므로 sum()결과는 O(1)의 시간이 걸림)

3 Druid Storage

3.1 Segment

segment는 데이터를 빠르게 읽기 위한 전용 데이터셋

Druid는 시간별로 분할된 세그먼트 파일에 인덱스를 저장 (기본 설정으로는 각 시간 간격마다 하나의 segment) 세그먼트 파일의 크기는 각각 300MB~700MB 내에 있는 것을 권장함. (이 범위보다 크면 시간 간격을 더 세분화하거나 데이터를 파티셔닝하고 PartitionSize를 조정하는것이 필요)

세그먼트는 Druid의 기본 저장 단위. 클러스터 내 데이터 복제 및 분산은 모두 segment 단위로 이루어짐. segment 데이터는 변경될 수 없는 상태이며, 이 덕분에 읽기/쓰기 동작 시에 경합이 없음.

3.2 Sharding

Druid는 시계열 정보를 가진 데이터를 여러 shard로 나누어 분할 저장. 세그먼트 사본 중 하나가 사라지면 브로커는 해당 세그먼트의 모든 기존 사본에 쿼리를 전달하여 대응.

이런 세그먼트 분할은 병렬 처리를 위한 핵심 역할을 함. 여러 세그먼트에 여러 CPU가 동시에 병렬적으로 데이터를 스캔하기 때문에 쿼리 결과를 신속하게 반환할 수 있음.

3.3 Storage Format

데이터를 저장하는 방식은 Druid를 분석 쿼리에 최적화하는 핵심 요소 중 하나입니다.

Druid는 컬럼 기반으로 데이터를 저장합니다. 컬럼 기반으로 데이터를 저장하면 압축률이 높아지므로 스토리지 리소스를 줄일 수 있고 , 집계하는 과정에서 특정 컬럼만 사용하기 때문에 CPU 리소스를 줄일 수 있습니다.

4 데이터 수집 (Ingestion / Indexing)

4.1 데이터 수집

  • druid에 데이터를 로드하는 것을 ingestion 또는 indexing이라 합니다. Druid로 데이터를 수집할 때, Druid는 소스 시스템에서 데이터를 읽고 세그먼트라는 데이터 파일에 저장합니다.
  • 대부분의 수집은 Druid MiddleManager process 또는 Indexer process가 소스 데이터를 로드합니다. 유일한 예외는 Hadoop기반 수집인데, YARN에서 Hadoop MapReduce 작업을 사용합니다

Druid는 batch와 real-time 데이터 수집을 지원합니다.

  • 배치

    • 수집하는 동안(During ingestion,) Druid는 세그먼트를 생성하고 이를 딥 스토리지에 저장합니다. 히스토리컬 노드는 쿼리에 응답하기 위해 세그먼트를 메모리에 로드합니다.
  • 실시간

    • 스트리밍 수집의 경우, 미들 매니저와 인덱서(indexer)는 실시간으로 유입되는 데이터로 바로 쿼리에 응답할 수 있습니다.

    실시간 데이터 수집(ingestion)을 위해 real-time Node가 있으며, 이 노드에 저장되는 데이터 스트림 내 이벤트들은 수 초 이내에 Druid 클러스터에 쿼리가 가능한 포맷으로 인덱싱됩니다.

5 데이터 Roll-up

Druid는 수집 단계에서 데이터를 롤업하여 디스크에 저장할 raw 데이터의 양을 줄일 수 있습니다. 롤업은 요약이나 사전 집계(pre-aggregation)의 한 종류입니다. 데이터를 롤업하면 저장할 데이터의 크기를 줄이고 row 수를 크게는 몇 배까지 줄일 수 있습니다. 롤업의 효율성은 높힐 수 있으나, 개별 이벤트들에 대해서는 쿼리를 날릴 수 없습니다.

rollup은 원천 데이터 저장 용량을 최소화시켜 스토리지에 대한 리소스를 절약하며 쿼리 속도를 빠르게 할 수 있습니다.

Column에는 비슷한 데이터가 모여 있을 확률이 높기 때문에 dictionary encoding과 같은 방법을 사용하여 스트링을 하나의 정수로 매핑할 수 있다면 데이터를 저장하는 리소스를 압축할 수도 있습니다.

6 Arcutecture

  • Druid 클러스터는 여러 유형의 노드군으로 구성되며, 각 유형의 노드군별로 고유의 역할을 수행합니다.

6.1 Deep Storage

  • Deep storage 란, Segment가 저장되는 곳으로 External storage와 같은 개념으로 생각하면 된다. ( 일반적으로 Hadoop 을 사용하는 것 같음 )
  • Deep storage에 데이터를 저장함으로써 높은 데이터 내구성을 제공한다.

Deep storage 는 아래 타입과 호환이 가능하다.

  • Local
  • Amazon S3
  • 구글 클라우드 저장소
  • Azure Blob 스토리지
  • HDFS

6.2 Real-time 노드

  • Real-time 노드군은 이벤트 스트림을 ingestion하고 쿼리하는 기능을 함.
    • 주로 스트리밍 데이터 파이프라인과 연동하여 데이터를 인덱싱하고, 처리된 데이터를 세그먼트로 저장
  • 이 노드들은 최근 발생한 짧은 시간 범위 내 이벤트들만을 처리하며, 주기적으로 이들을 딥 스토리지로 넘김

  1. 유입되는 이벤트들은 메모리에 인덱싱되면서 즉시 쿼리에 사용될 수 있음
  2. 메모리 상의 데이터는 정기적으로 디스크에 저장되면서 수정 불가능한(읽기 전용) 컬럼형 포맷으로 변환됩니다.
  3. 저장된 데이터는 off-heap 메모리로 로드되기 때문에 쿼리 가능한 상태가 유지됩니다.
  4. 디스크에 저장된 인덱스들을 주기적으로 병합되어 데이터 '세그먼트'를 구성한 후 딥 스토리지로 이관됩니다.

이런 식으로 real-time 노드로 ingestion된 모든 이벤트는 디스크 저장 전후를 막론하고 on-heap 또는 off-heap 메모리 상에 존재하므로 쿼리가 가능한 상태를 유지합니다(쿼리는 메모리 상의 인덱스와 디스크에 저장된 인덱스 모두에 전달됩니다).

이러한 real-time 노드 기능을 통해 Druid는 실시간 데이터 ingestion을 수행할 수 있습니다. 즉, 이벤트들이 발생하면 곧 이어서 쿼리 대상이 됩니다. 그리고 이러한 과정에서 데이터 손실이 발생하지 않습니다.

6.3 Historical 노드

  • Historical 노드는 저장된 데이터를 관리하고, 사용자 쿼리에 대해 빠르게 결과를 제공하는 역할을 합니다. 쉽게 말해, 과거 데이터를 전문적으로 다루는 서버 라고 보면 뙴
  • historical 노드군은 real-time 노드가 생성한 읽기 전용 데이터 블록(세그먼트)을 로드하고 처리하는 기능을 합니다.
  • 이 노드들은 딥 스토리지에서 읽기 전용 세그먼트를 다운로드하고 이에 대한 쿼리를 처리합니다(예: 데이터 집계/필터링). 이 노드들은 shared-nothing 아키텍쳐에 기반하며 동작이 단순합니다. 이들 간에는 경합이 발생하지 않으며 단순히 Zookeeper의 지시에 따라 세그먼트를 로드, 드롭, 처리할 뿐입니다.

historical 노드가 쿼리를 처리하는 프로세스는 다음과 같습니다.

  • 쿼리를 받으면 historical 노드는 우선 자신에게 이미 어떤 세그먼트가 존재하는지에 관한 정보를 보관하는 로컬 캐시를 확인합니다.
  • 어떤 세그먼트에 관한 정보가 캐시에 없으면 노드는 딥 스토리지에서 해당 세그먼트를 다운로드합니다. 그런 다음, 해당 세그먼트는 Zookeeper에서 선언되어 쿼리가 가능한 대상이 되며, 노드는 이 세그먼트에 대해 요청된 쿼리를 수행합니다.
  • historical 노드는 읽기 전용 데이터만을 다루므로 read consistency를 보장할 수 있습니다. 읽기 전용 데이터 블록들은 또한 단순한 병렬 모델을 가능케 합니다. 즉, historical 노드들은 읽기 전용 데이터 블록들을 서로 간섭하지 않고 동시에 스캔·집계할 수 있습니다.

real-time 노드와 마찬가지로 historical 노드들도 자신들의 온라인 상태와 처리 중인 데이터를 Zookeeper에 보고합니다.

6.4 Broker 노드

  • broker 노드군은 Zookeeper에 보고된 메타데이터를 통해 어떤 세그먼트들이 쿼리 가능한지와 이 세그먼트들이 각각 어디에 저장되어 있는지를 파악합니다.
  • broker 노드들은 입력된 쿼리들의 경로를 지정함으로써 각 쿼리가 올바른 historical 또는 real-time 노드에 도달되게끔 합니다. 그런 다음 historical 및 real-time 노드 각각에서 산출된 결과들을 취합하여 최종 쿼리 결과를 호출자에게 반환합니다.

broker 노드는 리소스 효율성을 높이기 위해 다음과 같이 캐시를 사용합니다.

-어떤 쿼리가 여러 세그먼트를 포괄할 경우 broker 노드는 캐시에 이미 존재하는 세그먼트들을 우선 확인합니다. 그리고 캐시에 없는 세그먼트들에 대해서는 그것이 보관된 historical 및 real-time 노드로 쿼리를 전달합니다.

historical 노드들이 결과를 반환하면, broker 노드는 이 결과를 나중에 사용할 수 있도록 세그먼트별로 캐시에 저장합니다. real-time 노드의 데이터는 캐시에 저장되지 않으며, 따라서 real-time 데이터에 대한 요청은 항상 real-time 노드로 전달됩니다. real-time 노드의 데이터는 가변적이기 때문에 그 결과를 캐시에 저장하는 것은 안정적이지 않기 때문입니다.

6.5 Coordinator 노드

coordinator 노드군은 주로 historical 노드 데이터의 관리 및 분산을 담당합니다. coordinator 노드는 어떤 historical 노드가 어떤 세그먼트에 대해 쿼리를 수행할지 결정하고 이들에게 새 데이터를 로드하고, 기한이 지난 데이터를 드롭하고, 데이터를 복제하고, 데이터를 이동하여 부하 밸런스를 맞추도록 지시합니다.

이렇게 함으로써 분산형 historical 노드 그룹에서 빠르고 효율적이며 안정으로 데이터를 처리할 수 있습니다.

다른 모든 Druid 노드와 마찬가지로, coordinator 노드들도 Zookeeper 연결을 유지함으로써 클러스터의 현황을 파악합니다. coordinator 노드들은 MySQL 데이터베이스와의 연결도 유지하는데, 이 데이터베이스에서는 클러스터 내 세그먼트의 생성, 소멸, 복제 규칙과 같은 추가적인 연산 매개변수 및 구성 정보를 관리합니다.

Druid 클러스터의 안정성을 위해 coordinator 노드는 이중화되며 일반적으로 하나의 coordinator 노드만 활성 상태를 유지합니다.

6.6 외부 종속 모듈

Druid는 클러스터 동작을 위해 몇 가지 외부 종속 모듈을 사용합니다.

  • Zookeeper: Druid는 Zookeeper를 통해 클러스터 내부 통신을 합니다.
  • 메타데이터 스토리지: Druid는 메타데이터 스토리지를 통해 데이터 세그먼트 및 구성에 관한 메타데이터를 저장합니다. 메타데이터 스토리지로는 주로 MySQL과 PostgreSQL이 사용됩니다.
profile
3년차 데이터 분석가

0개의 댓글