
사용자가 데이터를 실시간으로 탐색하고 시각화할 수 있는 시스템을 구현하고자 했음. 이 시스템은 다음과 같은 요구사항이 있었습니다.
데이터 탐색 입장에서의 요구사항
데이터 수집 입장에서의 요구사항
시스템상에서의 요구사항
이러한 필요를 충족하기 위해, Druid라는 데이터 엔진이 탄생. Druid는 대규모 트랜잭션 이벤트(로그 데이터)를 실시간으로 수집, 인덱싱, 분석하고, 이를 기반으로 빠르고 신뢰성 높은 쿼리 응답을 제공함.


아파치 드루이드는 이러한 다차원 데이터를 빠른 쿼리속도를 제공하기 위해 세그먼트 단위로 데이터를 인덱스하여 저장하고. 이렇게 세그먼트를 만드는 것은 지정한 시간(time interval) 단위 이고 쿼리를 수행할 때가 아니라 데이터를 적재할때 함께 인덱싱.
이러한 특징으로 인해서 드루이드는 전통적인 데이터베이스들에 비해서 쿼리 시간이 엄청나게 빠름. (예를 들어 전통적인 데이터베이스에 1억건의 데이터가 있다고 가정할 때 sum()에 대한 결과를 가져오는데 O(n)의 시간 복잡도가 걸림 반면에 드루이드 경우에는 미리 디멘젼(Dimension)을 지정해놓으면 데이터를 넣을 때 마다 인덱싱이 일어나므로 sum()결과는 O(1)의 시간이 걸림)
segment는 데이터를 빠르게 읽기 위한 전용 데이터셋

Druid는 시간별로 분할된 세그먼트 파일에 인덱스를 저장 (기본 설정으로는 각 시간 간격마다 하나의 segment) 세그먼트 파일의 크기는 각각 300MB~700MB 내에 있는 것을 권장함. (이 범위보다 크면 시간 간격을 더 세분화하거나 데이터를 파티셔닝하고 PartitionSize를 조정하는것이 필요)
세그먼트는 Druid의 기본 저장 단위. 클러스터 내 데이터 복제 및 분산은 모두 segment 단위로 이루어짐. segment 데이터는 변경될 수 없는 상태이며, 이 덕분에 읽기/쓰기 동작 시에 경합이 없음.


Druid는 시계열 정보를 가진 데이터를 여러 shard로 나누어 분할 저장. 세그먼트 사본 중 하나가 사라지면 브로커는 해당 세그먼트의 모든 기존 사본에 쿼리를 전달하여 대응.
이런 세그먼트 분할은 병렬 처리를 위한 핵심 역할을 함. 여러 세그먼트에 여러 CPU가 동시에 병렬적으로 데이터를 스캔하기 때문에 쿼리 결과를 신속하게 반환할 수 있음.

데이터를 저장하는 방식은 Druid를 분석 쿼리에 최적화하는 핵심 요소 중 하나입니다.
Druid는 컬럼 기반으로 데이터를 저장합니다. 컬럼 기반으로 데이터를 저장하면 압축률이 높아지므로 스토리지 리소스를 줄일 수 있고 , 집계하는 과정에서 특정 컬럼만 사용하기 때문에 CPU 리소스를 줄일 수 있습니다.

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

Druid는 수집 단계에서 데이터를 롤업하여 디스크에 저장할 raw 데이터의 양을 줄일 수 있습니다. 롤업은 요약이나 사전 집계(pre-aggregation)의 한 종류입니다. 데이터를 롤업하면 저장할 데이터의 크기를 줄이고 row 수를 크게는 몇 배까지 줄일 수 있습니다. 롤업의 효율성은 높힐 수 있으나, 개별 이벤트들에 대해서는 쿼리를 날릴 수 없습니다.
rollup은 원천 데이터 저장 용량을 최소화시켜 스토리지에 대한 리소스를 절약하며 쿼리 속도를 빠르게 할 수 있습니다.
Column에는 비슷한 데이터가 모여 있을 확률이 높기 때문에 dictionary encoding과 같은 방법을 사용하여 스트링을 하나의 정수로 매핑할 수 있다면 데이터를 저장하는 리소스를 압축할 수도 있습니다.

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

이런 식으로 real-time 노드로 ingestion된 모든 이벤트는 디스크 저장 전후를 막론하고 on-heap 또는 off-heap 메모리 상에 존재하므로 쿼리가 가능한 상태를 유지합니다(쿼리는 메모리 상의 인덱스와 디스크에 저장된 인덱스 모두에 전달됩니다).
이러한 real-time 노드 기능을 통해 Druid는 실시간 데이터 ingestion을 수행할 수 있습니다. 즉, 이벤트들이 발생하면 곧 이어서 쿼리 대상이 됩니다. 그리고 이러한 과정에서 데이터 손실이 발생하지 않습니다.

historical 노드가 쿼리를 처리하는 프로세스는 다음과 같습니다.
real-time 노드와 마찬가지로 historical 노드들도 자신들의 온라인 상태와 처리 중인 데이터를 Zookeeper에 보고합니다.
broker 노드는 리소스 효율성을 높이기 위해 다음과 같이 캐시를 사용합니다.

-어떤 쿼리가 여러 세그먼트를 포괄할 경우 broker 노드는 캐시에 이미 존재하는 세그먼트들을 우선 확인합니다. 그리고 캐시에 없는 세그먼트들에 대해서는 그것이 보관된 historical 및 real-time 노드로 쿼리를 전달합니다.
historical 노드들이 결과를 반환하면, broker 노드는 이 결과를 나중에 사용할 수 있도록 세그먼트별로 캐시에 저장합니다. real-time 노드의 데이터는 캐시에 저장되지 않으며, 따라서 real-time 데이터에 대한 요청은 항상 real-time 노드로 전달됩니다. real-time 노드의 데이터는 가변적이기 때문에 그 결과를 캐시에 저장하는 것은 안정적이지 않기 때문입니다.
coordinator 노드군은 주로 historical 노드 데이터의 관리 및 분산을 담당합니다. coordinator 노드는 어떤 historical 노드가 어떤 세그먼트에 대해 쿼리를 수행할지 결정하고 이들에게 새 데이터를 로드하고, 기한이 지난 데이터를 드롭하고, 데이터를 복제하고, 데이터를 이동하여 부하 밸런스를 맞추도록 지시합니다.
이렇게 함으로써 분산형 historical 노드 그룹에서 빠르고 효율적이며 안정으로 데이터를 처리할 수 있습니다.
다른 모든 Druid 노드와 마찬가지로, coordinator 노드들도 Zookeeper 연결을 유지함으로써 클러스터의 현황을 파악합니다. coordinator 노드들은 MySQL 데이터베이스와의 연결도 유지하는데, 이 데이터베이스에서는 클러스터 내 세그먼트의 생성, 소멸, 복제 규칙과 같은 추가적인 연산 매개변수 및 구성 정보를 관리합니다.
Druid 클러스터의 안정성을 위해 coordinator 노드는 이중화되며 일반적으로 하나의 coordinator 노드만 활성 상태를 유지합니다.
Druid는 클러스터 동작을 위해 몇 가지 외부 종속 모듈을 사용합니다.