본문 바로가기
빅데이터(Big Data)

ClickHouse vs Apache Doris: 왜 우리는 Doris를 선택했는가?

by forward error correction Circle 2026. 7. 17.
반응형

1. Apache Doris 기술이란?

왜 필요한가 — 기존 방식의 한계

실무에서 분석 파이프라인을 굴려 본 사람이라면 다음 패턴을 익숙하게 마주합니다.

  • ClickHouse의 분산 JOIN 한계 — 단일 노드 성능은 무시무시하지만, 큰 팩트 테이블끼리 분산 JOIN을 던지면 Distributed 테이블의 GLOBAL JOIN이 폭주하거나, Colocate 키를 미리 못 맞춰서 데이터 셔플로 무너짐
  • Hive/Trino 스택의 분 단위 지연 — Hive Metastore + Trino + S3 조합은 강력하지만, 데이터가 "지금 막 들어왔는데 1초 안에 보고 싶다"는 요구를 만족시키기 어려움
  • Snowflake/BigQuery의 단가 — 잘 만들어진 SaaS지만, 대시보드 한 장이 분당 수백 번 갱신되는 운영 환경에서는 크레딧/쿼리 단가가 빠르게 누적
  • Lambda 아키텍처의 복잡성 — 실시간(Kafka→Flink→OLAP) + 배치(S3→Spark→OLAP)를 따로 굴리면, 두 결과가 미세하게 안 맞아 디버깅에 일주일이 사라짐
  • 차원/사실 모델링 강요 — 분석 워크로드가 다양해지는데, 컬럼 OLAP은 사전 집계와 비정규화가 강제되어 모델 변경 시 ETL 폭탄

기술 정의

Apache Doris는 Baidu에서 출발해 ASF Top-Level 프로젝트로 자리 잡은 오픈소스 실시간 MPP 분석 데이터베이스입니다. 표준 MySQL 프로토콜을 그대로 쓰면서, 내부적으로는 FE(Java, 메타데이터/플래너)와 BE(C++, 저장/실행)가 분리된 진짜 MPP 구조를 갖습니다. Aggregate / Unique / Duplicate 세 가지 데이터 모델, 비동기 머터리얼라이즈드 뷰 + 자동 쿼리 재작성, 벡터화 파이프라인 실행 엔진, Iceberg/Hudi/Hive 다중 카탈로그를 한 바이너리에 모아둔 것이 Doris의 정체성입니다.

핵심 한 줄 정의
Apache Doris = MySQL 프로토콜 호환 MPP OLAP DB. FE/BE 분리, 벡터화 파이프라인 실행, 비동기 MV 자동 재작성, 멀티 카탈로그 페더레이션을 한 클러스터에 통합.

2. Apache Doris 기술 특징

특징 설명
True MPP FE가 분산 실행 플랜을 만들고 BE들이 병렬로 처리.
분산 Hash/Broadcast/Bucket Shuffle JOIN 모두 지원.
Pipeline Execution 2.x부터 도입된 비동기 파이프라인 실행 엔진. CPU 코어를 풀로 활용, 동시성 폭주에도 안정적.
Vectorized Engine SIMD 기반 컬럼 벡터 처리. 행 단위 처리 대비 5~10배 처리량.
3가지 데이터 모델 Aggregate(사전 집계), Unique(Primary Key, Upsert), Duplicate(원본 보존).
워크로드별 선택 가능.
Async Materialized View 2.1+ 비동기 MV. 옵티마이저가 SQL을 자동 재작성해 MV로 라우팅. dbt와 결합 자연스러움.
Multi-Catalog Iceberg, Hudi, Paimon, Hive, JDBC, Elasticsearch를 외부 카탈로그로 마운트해 페더레이션 쿼리.
Storage-Compute Decoupling 3.x의 Compute Group 모드. S3/오브젝트 스토리지에 데이터 두고 BE를 워크로드별 그룹으로 분리.
MySQL Protocol MySQL 클라이언트/JDBC/ORM 그대로 사용. BI 툴(Superset, Tableau, Metabase) 즉시 연결.
Stream Load / Routine Load HTTP PUT 기반 마이크로 배치 ingest, Kafka 직결 Routine Load. 별도 ETL 미들웨어 없이 실시간 적재.
Workload Group CPU/메모리/IO 쿼터를 그룹별로 격리. 무거운 ETL이 BI 대시보드를 죽이는 사고 방지.

3. Apache Doris 동작방식

ⅰ. 구성 요소

컴포넌트 언어 역할
FE (Frontend) Java 메타데이터 관리(BDB-JE 기반 Raft-like), SQL 파싱/플래닝/옵티마이저(Nereids), MySQL 프로토콜 게이트웨이. Master / Follower / Observer 역할.
BE (Backend) C++ 실제 데이터 저장(Tablet 단위 컬럼 저장), 쿼리 실행, Compaction, 데이터 적재.
Tablet / Replica 테이블은 Partition → Bucket → Tablet으로 쪼개짐. Tablet은 보통 3 Replica로 BE에 분산 저장.
Nereids Planner Java 2.x부터 기본 채택된 새 코스트 기반 옵티마이저. CBO + 룰 기반 + MV 재작성 통합.
Pipeline Engine C++ BE 내부의 비동기 실행 엔진. Operator를 파이프라인으로 묶어 코어 활용 극대화.
CN (Compute Node) C++ 3.x 분리형 모드의 무상태 실행 노드. 데이터는 S3에, 캐시는 로컬에. 탄력적 스케일.
Broker Java HDFS/S3 등 원격 스토리지 접근용 보조 데몬. Broker Load와 백업/복원에서 사용.

ⅱ. 데이터 흐름 — 쓰기 경로 (Stream Load)

  1. 클라이언트가 HTTP PUT으로 FE에 batch를 보냄 (CSV/JSON/Parquet)
  2. FE가 적재 트랜잭션 ID를 발급하고 적절한 BE를 Coordinator로 지정해 redirect
  3. Coordinator BE가 데이터를 파싱 → 각 Tablet의 리더 Replica에 분배 (Hash by Bucket Key)
  4. 각 BE가 메모리 MemTable에 누적 → flush 시 RowSet(컬럼 파일 묶음)으로 디스크 기록
  5. 3개 Replica 중 정족수(Quorum, 기본 2/3) 성공하면 Publish → 가시성 확보
  6. 백그라운드에서 Compaction이 작은 RowSet들을 큰 단위로 병합

ⅲ. 데이터 흐름 — 읽기 경로 (SELECT)

  1. MySQL 프로토콜로 FE에 SQL 도착
  2. Nereids 파서 → 분석 → 룰 기반 변환(컬럼 프루닝, 술어 푸시다운, MV 재작성) → CBO로 분산 플랜 결정
  3. FE가 BE 단편(Fragment)을 만들어 각 BE에 송신 → BE의 Pipeline Engine이 병렬 실행
  4. BE 간 셔플(Hash Exchange)이 필요한 단계는 Brpc로 데이터 교환
  5. 최종 결과는 Coordinator BE에 모이고 FE를 거쳐 클라이언트로 스트리밍

4. Apache Doris 구성 및 흐름도

ⅰ. 전체 구성도

┌───────────────────────────────────────────────────────────────┐
│  Clients: BI Tools / JDBC / MySQL CLI / curl(Stream Load)     │
└──────────────────────────┬────────────────────────────────────┘
                           │ MySQL Protocol :9030 / HTTP :8030
                           ▼
   ┌───────────────────────────────────────────────────────┐
   │   FE Cluster (Java)                                   │
   │   ┌──────────┐  ┌──────────┐  ┌──────────┐            │
   │   │  Master  │◄►│ Follower │◄►│ Follower │  Observer  │
   │   └──────────┘  └──────────┘  └──────────┘            │
   │   - Metadata (BDB-JE Raft-like)                       │
   │   - Nereids Planner / CBO / MV Rewrite                │
   │   - Catalog Manager (Internal + Iceberg/Hive/JDBC)    │
   └─────────────────────────┬─────────────────────────────┘
                             │ Brpc Fragment Dispatch
            ┌────────────────┼────────────────┐
            ▼                ▼                ▼
      ┌──────────┐     ┌──────────┐     ┌──────────┐
      │   BE 1   │     │   BE 2   │     │   BE 3   │
      │  (C++)   │◄───►│  (C++)   │◄───►│  (C++)   │
      │ Pipeline │     │ Pipeline │     │ Pipeline │
      │  Engine  │     │  Engine  │     │  Engine  │
      └────┬─────┘     └────┬─────┘     └────┬─────┘
           │ Tablets        │ Tablets        │ Tablets
           ▼                ▼                ▼
      ┌─────────────────────────────────────────────┐
      │  Local Storage (Segment files, Columnar)    │
      │  or  Object Storage (S3/HDFS) [3.x 분리형]   │
      └─────────────────────────────────────────────┘

ⅱ. 단계별 설명

단계 처리 내용
① 접속 MySQL 클라이언트 또는 JDBC가 FE의 9030 포트(MySQL 프로토콜)로 연결. 인증/세션 변수 처리.
② 플래닝 Nereids가 SQL → AST → Logical Plan → Optimized Plan → Physical Plan을 만든 뒤, MV 재작성/Runtime Filter 삽입을 적용.
③ 분배 Plan을 Fragment로 쪼개 각 BE에 Brpc로 전송. Coordinator BE가 결정됨.
④ 실행 BE의 Pipeline Engine이 Operator를 파이프라인으로 연결해 SIMD 벡터 단위로 실행. 필요한 Tablet은 로컬 또는 S3 캐시에서 읽음.
⑤ 셔플/조인 분산 JOIN/집계 시 Hash Exchange로 BE 간 데이터 재분배. Colocate JOIN이면 셔플 생략.
⑥ 집계/반환 Final aggregation 후 Coordinator BE → FE → 클라이언트로 결과 스트리밍.

ⅲ. 실제 처리 흐름 — Kafka 실시간 적재 + BI 대시보드

[App] ─► [Kafka topic: events] ─► [Doris Routine Load Job]
                                          │
                                          ▼ Stream Load 내부 변환
                                  [BE Coordinator]
                                          │
                       ┌──────────────────┼──────────────────┐
                       ▼                  ▼                  ▼
                  [BE1 Tablet1]      [BE2 Tablet2]      [BE3 Tablet3]
                       │                  │                  │
                       ▼                  ▼                  ▼
                  MemTable Flush ──► RowSet ──► Compaction ──► Sorted Segment
                                          │
                       ┌──────────────────┴──────────────────┐
                       ▼                                     ▼
              [Async MV: events_daily_agg]         [Base Table: events]
                       │                                     │
                       └────────────┬────────────────────────┘
                                    ▼
                       [Superset / Metabase / Tableau]
                                    ▼
                       사용자 대시보드 (1~5초 지연)

5. Apache Doris 설치 방법

ⅰ. 최소 권장 사양

항목 PoC 프로덕션 (수십 TB)
FE 노드 1개 (4C/8G) 3개 (8C/16G, 1 Master + 2 Follower) 홀수개 필수
BE 노드 3개 (8C/16G, NVMe 200GB) ≥5개 (16C/64G, NVMe 2TB+, 별도 데이터 디스크)
OS CentOS 7+/RHEL 8+/Ubuntu 22.04 RHEL 8+/Ubuntu 22.04 LTS, 커널 4.18+
JVM (FE) OpenJDK 17 (필수) OpenJDK 17, ZGC 권장, Heap ≥ 8GB
네트워크 1GbE 10GbE 이상, 셔플 트래픽 큼

ⅱ. 바이너리 설치 (단일 노드 PoC)

# 1) 커널 파라미터 — 누락하면 BE가 OOM/ENOMEM으로 죽습니다
sudo sysctl -w vm.max_map_count=2000000
sudo sysctl -w vm.swappiness=0
ulimit -n 655350
echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

# 2) JDK 17 설치 (FE 필수)
sudo apt install -y openjdk-17-jdk
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

# 3) Doris 바이너리 다운로드 (예: 3.0.x)
wget https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-3.0.x-bin-x64.tar.gz
tar -xzf apache-doris-3.0.x-bin-x64.tar.gz && cd apache-doris-3.0.x-bin-x64

# 4) FE 기동
cd fe && bin/start_fe.sh --daemon
# FE 메타 디렉터리, edit_log_port=9010, query_port=9030 설정 필요

# 5) BE 기동
cd ../be && bin/start_be.sh --daemon

# 6) BE를 클러스터에 등록
mysql -h127.0.0.1 -P9030 -uroot -e "ALTER SYSTEM ADD BACKEND '127.0.0.1:9050';"
mysql -h127.0.0.1 -P9030 -uroot -e "SHOW BACKENDS\G"

 

ⅲ. Docker Compose / Kubernetes

테스트는 공식 Docker Compose 예제로 5분이면 충분합니다. 운영은 Doris Operator(공식 K8s 오퍼레이터)를 통해 DorisCluster CR로 선언하고, BE는 StatefulSet + Persistent Volume(NVMe 클래스)로 띄우는 것이 표준입니다. FE는 헤드리스 서비스 + 3개 Pod 홀수 구성, BE는 anti-affinity로 노드 분산이 필수입니다.

설치 시 흔한 실수 — 직접 겪은 케이스
  • FE 짝수 구성 — Follower를 2개로 두면 Master 선출이 한쪽 장애에 막힘. 반드시 1·3·5 홀수.
  • BE의 max_map_count 누락 — 로그에 mmap failed이 떨어지며 OOM처럼 보이지만 실제 원인은 vm.max_map_count.
  • Swap 활성화 상태로 BE 기동 — 컴팩션 중 시스템이 Swap에 데이터를 쓰며 latency가 100배 튐. swappiness=0 필수.
  • JDK 11 사용 — Doris 2.1+는 JDK 17 강제. 11로 띄우면 FE가 silent fail.

6. Apache Doris 사용 방법

ⅰ. 테이블 만들기 — 데이터 모델 선택

-- 1) 이벤트 원본을 그대로 저장 (Duplicate 모델)
CREATE TABLE events (
    event_time DATETIME NOT NULL,
    user_id    BIGINT,
    event_type VARCHAR(64),
    revenue    DECIMAL(18,4),
    payload    JSONB
)
DUPLICATE KEY(event_time, user_id)
PARTITION BY RANGE(event_time) ()
DISTRIBUTED BY HASH(user_id) BUCKETS 32
PROPERTIES (
    "replication_num" = "3",
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-30",
    "dynamic_partition.end"   = "3",
    "dynamic_partition.prefix"= "p",
    "dynamic_partition.buckets" = "32"
);

-- 2) Upsert가 필요한 사용자 프로필 (Unique Key 모델, MoW 권장)
CREATE TABLE user_profile (
    user_id     BIGINT,
    nickname    VARCHAR(64),
    tier        VARCHAR(16),
    last_login  DATETIME
)
UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "enable_unique_key_merge_on_write" = "true"
);

ⅱ. 실시간 적재 — Stream Load

# HTTP PUT 한 줄로 실시간 적재
curl --location-trusted -u admin:secret \
  -H "label:events_$(date +%s)" \
  -H "format:json" -H "strip_outer_array:true" \
  -T events_batch.json \
  http://fe-host:8030/api/mydb/events/_stream_load

ⅲ. Kafka 직결 — Routine Load

CREATE ROUTINE LOAD mydb.kafka_events ON events
COLUMNS(event_time, user_id, event_type, revenue, payload)
PROPERTIES (
    "desired_concurrent_number" = "6",
    "max_batch_interval" = "10",
    "max_batch_rows" = "200000",
    "format" = "json",
    "strip_outer_array" = "true"
)
FROM KAFKA (
    "kafka_broker_list" = "kafka1:9092,kafka2:9092",
    "kafka_topic" = "events",
    "property.group.id" = "doris-events",
    "property.kafka_default_offsets" = "OFFSET_END"
);

ⅳ. 비동기 머터리얼라이즈드 뷰 — 자동 재작성

CREATE MATERIALIZED VIEW events_daily_agg
BUILD IMMEDIATE REFRESH AUTO ON SCHEDULE EVERY 5 MINUTE
DISTRIBUTED BY HASH(d) BUCKETS 8
PROPERTIES ("replication_num" = "3")
AS
SELECT date_trunc(event_time, 'day') AS d,
       event_type,
       count(*)                       AS cnt,
       sum(revenue)                   AS revenue_sum
FROM events
GROUP BY 1, 2;

-- 사용자는 base table을 그대로 조회. Optimizer가 events_daily_agg로 자동 라우팅.
SET enable_materialized_view_rewrite = true;
SELECT date_trunc(event_time,'day'), event_type, sum(revenue)
FROM events
WHERE event_time >= '2026-04-01'
GROUP BY 1, 2;

ⅴ. 멀티 카탈로그 — Iceberg 페더레이션

CREATE CATALOG iceberg_lakehouse PROPERTIES (
    "type" = "iceberg",
    "iceberg.catalog.type" = "rest",
    "uri" = "http://lakekeeper:8181/catalog",
    "warehouse" = "s3://lake/warehouse",
    "s3.endpoint" = "https://s3.ap-northeast-2.amazonaws.com",
    "s3.access_key" = "AKIA...",
    "s3.secret_key" = "..."
);

SELECT i.product_id, sum(e.revenue)
FROM internal.mydb.events e
JOIN iceberg_lakehouse.gold.products i USING (product_id)
GROUP BY 1 ORDER BY 2 DESC LIMIT 100;

ⅵ. 운영 시 고려사항

항목 권장
Bucket 수 파티션당 데이터 1~10GB가 한 Tablet 크기에 맞도록 설정. 너무 적으면 핫스팟, 너무 많으면 메타 폭증.
Replica 프로덕션은 항상 3. 1로 두면 BE 한 대만 죽어도 쿼리가 깨짐.
Compaction Routine Load 부하가 높으면 compaction_task_num_per_disk를 6~8로 상향, 그래도 누적되면 적재 batch 크기를 키우는 게 근본.
Workload Group 최소 etl, bi 두 그룹으로 분리. ETL이 BI 동시성을 잡아먹는 사고 방지.
Backup S3 백업(BACKUP SNAPSHOT)을 일 단위 + CCR(2.x+)로 DR 사이트 복제.

7. Apache Doris 자주 쓰는 명령어

ⅰ. 클러스터 상태 확인

-- FE/BE 노드 헬스체크
SHOW FRONTENDS\G
SHOW BACKENDS\G

-- Tablet 분포 / 누락 / 비정상 Replica
ADMIN SHOW REPLICA STATUS FROM mydb.events;
SHOW TABLET FROM mydb.events;
ADMIN CHECK TABLET (10001, 10002) PROPERTIES("type"="consistency");

-- 진행 중인 적재/쿼리
SHOW LOAD ORDER BY CreateTime DESC LIMIT 20;
SHOW ROUTINE LOAD\G
SHOW PROC '/current_queries';
SHOW PROC '/cluster_balance';

ⅱ. 성능 진단

-- 실행 계획 (CBO 결정 확인)
EXPLAIN VERBOSE SELECT ...;

-- 실행 후 프로파일 — 어떤 Operator가 느린지
SET enable_profile = true;
SELECT ... ;
SHOW QUERY PROFILE "/";

-- Compaction 점수 (높으면 쌓여있다는 뜻)
SHOW PROC '/cluster_balance/cluster_load_stat/location_default';
-- 실행 계획 (CBO 결정 확인)
EXPLAIN VERBOSE SELECT ...;

-- 실행 후 프로파일 — 어떤 Operator가 느린지
SET enable_profile = true;
SELECT ... ;
SHOW QUERY PROFILE "/";

-- Compaction 점수 (높으면 쌓여있다는 뜻)
SHOW PROC '/cluster_balance/cluster_load_stat/location_default';

ⅲ. 사용자/권한/워크로드

# 1) 커널 파라미터 — 누락하면 BE가 OOM/ENOMEM으로 죽습니다
sudo sysctl -w vm.max_map_count=2000000
sudo sysctl -w vm.swappiness=0
ulimit -n 655350
echo madvise | sudo tee /sys/kernel/mm/transparent_hugepage/enabled

# 2) JDK 17 설치 (FE 필수)
sudo apt install -y openjdk-17-jdk
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

# 3) Doris 바이너리 다운로드 (예: 3.0.x)
wget https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-3.0.x-bin-x64.tar.gz
tar -xzf apache-doris-3.0.x-bin-x64.tar.gz && cd apache-doris-3.0.x-bin-x64

# 4) FE 기동
cd fe && bin/start_fe.sh --daemon
# FE 메타 디렉터리, edit_log_port=9010, query_port=9030 설정 필요

# 5) BE 기동
cd ../be && bin/start_be.sh --daemon

# 6) BE를 클러스터에 등록
mysql -h127.0.0.1 -P9030 -uroot -e "ALTER SYSTEM ADD BACKEND '127.0.0.1:9050';"
mysql -h127.0.0.1 -P9030 -uroot -e "SHOW BACKENDS\G"
CREATE USER bi_reader IDENTIFIED BY 'XXXXXXXX';
GRANT SELECT_PRIV ON mydb.* TO bi_reader;

-- 워크로드 그룹 격리
CREATE WORKLOAD GROUP bi
PROPERTIES ("cpu_share"="40","memory_limit"="40%","max_concurrency"="100");
SET PROPERTY FOR 'bi_reader' 'default_workload_group'='bi';

ⅳ. 실전 트러블슈팅

증상 원인 해결
BE OOM 반복 대형 GROUP BY/JOIN이 메모리 한계 초과. exec_mem_limit 기본 2GB는 운영용으로 너무 작음. 세션 변수로 상향 + 디스크 스필 활성화(enable_spill=true). 워크로드 그룹별 메모리 쿼터로 한 쿼리가 BE 전체를 잡아먹지 않게 격리.
Routine Load lag 폭증 batch 간격(max_batch_interval)이 너무 짧아 Compaction이 따라오지 못함. 작은 RowSet 폭증. batch 간격을 10~30초로 늘리고 행 수도 확대. SHOW PROC '/cluster_balance'로 Compaction 점수 모니터링.
분산 JOIN이 느림 두 큰 테이블이 같은 키로 분산되지 않아 매번 셔플 발생. Colocate Group을 묶어 같은 BE에 같은 Bucket을 위치시킴. 또는 작은 차원 테이블이면 Broadcast JOIN 힌트 사용.
MV 재작성 안 됨 쿼리 술어/조인/그룹키가 MV 정의보다 더 좁거나 다름. CBO가 매칭 못 함. EXPLAIN MEMO PLAN로 매칭 실패 이유 확인. MV를 더 일반화하거나 쿼리 술어를 MV 범위 안으로 조정.
FE Master 선출 실패 Follower 짝수 + 동시 장애. BDB-JE 정족수 미달. FE는 항상 홀수. 응급 시 metadata_failure_recovery=true로 단일 노드 복구 후 재구성.
타임존 결과 어긋남 FE 세션, BE OS, 클라이언트 세 곳의 TZ가 달라 DATETIME 결과가 +9시간 밀림. FE의 time_zone='Asia/Seoul' 글로벌 변수, BE OS TZ, JDBC URL serverTimezone 셋을 일치.
Tablet Unhealthy 다수 BE 다운/디스크 풀. 복제본 부족. ADMIN REPAIR TABLE 또는 BE 디스크 회수. 자동 균형(Tablet Scheduler)이 동작하도록 disable_balance=false 확인.
Iceberg 카탈로그 stale 외부 엔진(Spark/Flink)이 새 스냅샷을 만들었지만 Doris의 메타 캐시가 갱신 안 됨. REFRESH CATALOG iceberg_lakehouse; 또는 카탈로그 file.meta.cache.ttl-second을 짧게.

8. Apache Doris 활용방안

ⅰ. 대표 적용 사례

사례 내용
리얼타임 BI Kafka → Routine Load → Doris → Superset/Tableau. 1~5초 지연으로 어제 KPI가 아니라 "지금 KPI"를 보여줌.
고객 사용 분석 행위 이벤트 + 사용자 프로필을 분산 JOIN해 코호트 분석/퍼널 분석. ClickHouse에서는 까다로운 워크로드.
레이크하우스 가속 Iceberg/Hudi 위 데이터를 Doris가 바로 쿼리. 핫 데이터만 내부 테이블로 캐시해 대시보드 응답 1초 이내.
광고/추천 피처스토어 Unique Key + MoW로 사용자 피처 Upsert, 서빙단에서 단건 lookup + 집계 동시.
로그/관측성 OLAP Inverted Index + Bloom Filter로 로그 검색. ELK 비용 절감 + SQL 인터페이스.

ⅱ. 대안 기술과의 비교

기준 Apache Doris ClickHouse StarRocks Snowflake
아키텍처 FE/BE MPP 단일 노드 강함, 분산은 Distributed 테이블 Doris 포크, FE/BE 유사 SaaS, 분리형
분산 JOIN 강함 (Colocate, Bucket Shuffle, Broadcast) 제한적 (GLOBAL JOIN 비용 큼) 강함 (Doris와 동급) 매우 강함
실시간 ingest Stream/Routine Load 내장 Kafka Engine, 다소 까다로움 Routine Load 내장 Snowpipe (분 단위)
비동기 MV 자동 재작성 지원 제한적 MV (자동 재작성 약함) 자동 재작성 지원 Dynamic Tables
운영 모델 셀프호스팅, K8s 오퍼레이터 셀프호스팅, ClickHouse Cloud 셀프호스팅, CelerData Cloud 완전 SaaS
비용 하드웨어 비용만 하드웨어 비용만 하드웨어 비용만 크레딧 단가 높음
적합도 실시간 + JOIN + MV가 모두 필요할 때 사전 모델링된 시계열/이벤트 Doris와 유사, 일부 기능 우위 대규모 엔터프라이즈 DW

ⅲ. 언제 쓰면 안 되는가

  • OLTP 워크로드 — 단건 INSERT/UPDATE가 초당 수만 건 들어오는 시스템에는 부적합. 트랜잭션 격리가 OLTP 수준이 아님. 이 경우 PostgreSQL/MySQL/TiDB 사용.
  • 다중 행 트랜잭션 — 다수 테이블에 걸친 ACID 트랜잭션이 필요하면 Doris의 보장 수준은 부족. 일반 RDB로.
  • 풀텍스트 검색 메인 엔진 — Inverted Index가 있지만 OpenSearch/Elasticsearch 수준의 랭킹/한국어 형태소 분석은 약함. 단순 로그 검색 정도까지.
  • 초대형 단발성 ETL — 페타바이트급 ad-hoc 배치 변환은 Spark/Trino + Iceberg가 더 자연스러움. Doris는 자주 도는 분석에 강점.
  • 벡터 검색 메인 엔진 — 벡터 인덱스 기능이 있지만 Qdrant/Milvus처럼 RAG 전용 엔진의 latency/필터링/HNSW 튜닝 자유도에는 미치지 못함.

ⅳ. 트레이드오프 정리

얻는 것 대신 감수해야 하는 것
강력한 분산 JOIN과 표준 SQL 단일 노드 ClickHouse 대비 단순 단건 쿼리는 살짝 느림
실시간 ingest + 자동 MV 재작성 Routine Load + Compaction 튜닝 학습 곡선
멀티 카탈로그 페더레이션 외부 카탈로그 메타 캐시 운영 부담
셀프호스팅으로 비용 통제 Snowflake처럼 자동 스케일링/관리는 없음 (오퍼레이터 운영 필요)
MySQL 프로토콜로 BI 즉시 연결 MySQL이 아니므로 일부 SQL 방언/함수는 미지원



반응형