Apache Druid
Apache Druid
실시간 분석 데이터베이스. 서브초 쿼리 응답.
Apache Druid
실시간 분석 데이터베이스. 서브초 쿼리 응답.
Apache Druid는 대규모 데이터셋에서 실시간 OLAP(Online Analytical Processing) 쿼리를 서브초(sub-second) 응답 시간으로 처리하도록 설계된 고성능 분석 데이터베이스입니다. Metamarkets에서 개발되어 2012년 오픈소스로 공개되었으며, Netflix, Airbnb, eBay 등 대규모 트래픽을 처리하는 기업들이 사용합니다.
Druid의 핵심 아키텍처는 컬럼형 저장, 역색인(inverted index), 비트맵 인덱스를 결합하여 빠른 필터링과 집계를 가능하게 합니다. 데이터는 시간 기반으로 세그먼트로 분할되어 분산 저장되며, 이를 통해 시계열 데이터에 대한 효율적인 쿼리가 가능합니다.
실시간 데이터 수집과 배치 수집을 모두 지원합니다. Kafka, Kinesis 등의 스트리밍 소스에서 실시간으로 데이터를 수집하면서 동시에 HDFS, S3의 배치 데이터도 로드할 수 있습니다. 수집된 데이터는 자동으로 최적화된 세그먼트로 변환되어 딥 스토리지에 저장됩니다.
Druid SQL을 지원하여 표준 SQL 쿼리로 데이터를 분석할 수 있으며, Native JSON 쿼리도 제공됩니다. Superset, Tableau, Looker 등 주요 BI 도구와 연동되어 대시보드와 리포트 구축에 활용됩니다.
-- Apache Druid OLAP 쿼리 예제
-- ==========================================
-- 1. 기본 집계 쿼리 (시간 기반)
-- ==========================================
-- 일별 매출 집계
SELECT
TIME_FLOOR(__time, 'P1D') AS day,
COUNT(*) AS transaction_count,
SUM(amount) AS total_revenue,
COUNT(DISTINCT user_id) AS unique_users,
AVG(amount) AS avg_transaction
FROM sales_events
WHERE __time >= TIMESTAMP '2024-01-01'
AND __time < TIMESTAMP '2024-02-01'
GROUP BY TIME_FLOOR(__time, 'P1D')
ORDER BY day;
-- ==========================================
-- 2. 다차원 분석 (GROUPING SETS)
-- ==========================================
-- 카테고리, 지역별 복합 집계
SELECT
COALESCE(category, 'ALL') AS category,
COALESCE(region, 'ALL') AS region,
COUNT(*) AS events,
SUM(revenue) AS total_revenue
FROM page_views
WHERE __time >= CURRENT_TIMESTAMP - INTERVAL '7' DAY
GROUP BY GROUPING SETS (
(category, region),
(category),
(region),
()
)
ORDER BY category, region;
-- ==========================================
-- 3. 퍼널 분석 쿼리
-- ==========================================
-- 사용자 전환 퍼널
SELECT
step,
COUNT(DISTINCT user_id) AS users,
COUNT(DISTINCT user_id) * 100.0 /
FIRST_VALUE(COUNT(DISTINCT user_id)) OVER (ORDER BY step) AS conversion_rate
FROM (
SELECT
user_id,
MIN(CASE WHEN event_type = 'page_view' THEN 1 END) AS step1,
MIN(CASE WHEN event_type = 'add_to_cart' THEN 2 END) AS step2,
MIN(CASE WHEN event_type = 'checkout' THEN 3 END) AS step3,
MIN(CASE WHEN event_type = 'purchase' THEN 4 END) AS step4
FROM user_events
WHERE __time >= CURRENT_TIMESTAMP - INTERVAL '30' DAY
GROUP BY user_id
)
UNPIVOT (step FOR step_name IN (step1, step2, step3, step4))
WHERE step IS NOT NULL
GROUP BY step;
-- ==========================================
-- 4. 실시간 대시보드 쿼리
-- ==========================================
-- 최근 5분 실시간 메트릭
SELECT
TIME_FLOOR(__time, 'PT1M') AS minute,
datasource,
COUNT(*) AS requests_per_min,
APPROX_QUANTILE_DS(response_time, 0.95) AS p95_latency,
APPROX_QUANTILE_DS(response_time, 0.99) AS p99_latency
FROM request_logs
WHERE __time >= CURRENT_TIMESTAMP - INTERVAL '5' MINUTE
GROUP BY 1, 2
ORDER BY 1 DESC;
-- ==========================================
-- 5. 데이터 수집 스펙 (Kafka Ingestion)
-- ==========================================
-- JSON 형식의 Kafka Ingestion Spec
/*
{
"type": "kafka",
"spec": {
"dataSchema": {
"dataSource": "sales_events",
"timestampSpec": {
"column": "event_time",
"format": "iso"
},
"dimensionsSpec": {
"dimensions": [
"user_id",
"product_id",
"category",
{"name": "price", "type": "double"},
"region"
]
},
"metricsSpec": [
{"type": "count", "name": "count"},
{"type": "longSum", "name": "quantity", "fieldName": "quantity"},
{"type": "doubleSum", "name": "revenue", "fieldName": "revenue"},
{"type": "HLLSketchBuild", "name": "unique_users", "fieldName": "user_id"}
],
"granularitySpec": {
"type": "uniform",
"segmentGranularity": "HOUR",
"queryGranularity": "MINUTE",
"rollup": true
}
},
"ioConfig": {
"topic": "sales-events",
"consumerProperties": {
"bootstrap.servers": "kafka:9092"
},
"taskCount": 3,
"replicas": 2
},
"tuningConfig": {
"type": "kafka",
"maxRowsInMemory": 100000,
"maxRowsPerSegment": 5000000
}
}
}
*/
PM: "실시간 대시보드가 너무 느려요. 사용자가 10초 이상 기다리고 있어요."
데이터 엔지니어: "현재 Elasticsearch로 집계하고 있는데 데이터 양이 너무 많아졌어요. Druid로 마이그레이션하면 서브초 응답이 가능합니다."
테크리드: "Kafka에서 직접 실시간 수집 가능하죠? 지연 시간은 어느 정도 나오나요?"
데이터 엔지니어: "네, 수집 지연은 보통 1분 이내예요. 롤업으로 사전 집계하면 쿼리 속도가 더 빨라집니다."
면접관: "Druid와 ClickHouse의 차이점을 설명해주세요."
지원자: "둘 다 OLAP용 컬럼형 DB지만, Druid는 시계열 데이터와 실시간 수집에 더 최적화되어 있고, ClickHouse는 더 범용적인 분석 쿼리에 강합니다. Druid는 롤업을 통한 사전 집계가 기본이고, ClickHouse는 원시 데이터를 더 많이 보존합니다."
면접관: "Druid에서 데이터 보존 정책은 어떻게 관리하나요?"
지원자: "Coordinator에서 retention rules를 설정합니다. 최근 데이터는 Historical 노드에, 오래된 데이터는 cold tier나 삭제하도록 규칙을 정의할 수 있습니다."
리뷰어: "이 쿼리에서 COUNT(DISTINCT user_id)를 쓰고 있는데, 대용량에서 느릴 수 있어요."
개발자: "정확한 카운트가 필요해서 그렇게 했는데, 대안이 있나요?"
리뷰어: "정확도가 조금 낮아도 되면 APPROX_COUNT_DISTINCT나 HyperLogLog 스케치를 사용하세요. 수집 시 HLLSketchBuild로 미리 계산해두면 쿼리가 훨씬 빨라집니다."