Split View: 데이터 엔지니어링 완전 가이드 2025: Flink vs Spark, dbt, Iceberg, Airflow — 현대 데이터 스택 총정리
데이터 엔지니어링 완전 가이드 2025: Flink vs Spark, dbt, Iceberg, Airflow — 현대 데이터 스택 총정리
- 들어가며
- 1. 2025 데이터 엔지니어링 지형도
- 2. 스트림 처리: Flink vs Spark vs Beam
- 3. 오케스트레이션: Airflow 3.0 vs Dagster vs Prefect vs Mage
- 4. dbt: SQL의 부활
- 5. 레이크하우스: Iceberg vs Delta Lake vs Hudi
- 6. 실시간 분석: ClickHouse vs Druid vs Pinot vs StarRocks
- 7. Data Mesh와 Data Contracts
- 8. 실전 아키텍처: 실시간 추천 시스템
- 9. 자격증과 학습 로드맵
- 10. 포트폴리오 프로젝트 3개
- 퀴즈
- 참고 자료
들어가며
2025년, 데이터 엔지니어는 소프트웨어 업계에서 가장 수요가 높은 직군 중 하나가 되었습니다. AI/ML 파이프라인 구축 수요가 폭발하면서, 단순한 ETL 개발자에서 데이터 플랫폼 아키텍트로 역할이 확장되고 있습니다. LinkedIn의 2025 잡 리포트에 따르면 데이터 엔지니어 수요는 전년 대비 35% 증가했으며, 특히 스트리밍 처리와 레이크하우스 경험을 보유한 엔지니어의 연봉 프리미엄이 두드러집니다.
이 글에서는 **현대 데이터 스택(Modern Data Stack)**의 핵심 구성 요소를 빠짐없이 다룹니다. Flink vs Spark 스트림 처리 비교부터, dbt의 SQL 혁명, Iceberg 레이크하우스 전쟁, ClickHouse 실시간 분석, Airflow 3.0 오케스트레이션까지 — 실전 아키텍처와 함께 2025년 데이터 엔지니어링의 모든 것을 정리합니다.
1. 2025 데이터 엔지니어링 지형도
1.1 데이터 엔지니어가 가장 핫한 이유
2025년 데이터 엔지니어가 주목받는 세 가지 이유가 있습니다.
첫째, AI/ML 파이프라인 수요 폭발. ChatGPT 이후 모든 기업이 AI를 도입하려 하지만, 모델 훈련에 필요한 고품질 데이터 파이프라인을 구축할 수 있는 인력이 절대적으로 부족합니다. Gartner 추정에 따르면 ML 프로젝트의 85%가 데이터 품질 문제로 실패합니다.
둘째, 실시간 처리 요구 급증. 배치 처리만으로는 부족합니다. 사기 탐지, 실시간 추천, 동적 가격 책정 등 밀리초 단위 의사결정이 필요한 유스케이스가 폭발적으로 증가하고 있습니다.
셋째, 규제와 데이터 거버넌스. GDPR, AI Act 등 데이터 규제가 강화되면서, 데이터 리니지(lineage)와 품질 관리를 전문적으로 다룰 수 있는 엔지니어의 가치가 높아졌습니다.
1.2 현대 데이터 스택의 진화
현대 데이터 스택은 다음과 같은 계층 구조로 발전했습니다.
[데이터 소스] → [수집/CDC] → [스트림 처리] → [스토리지/레이크하우스] → [변환] → [분석/서빙]
| | | | | |
DB, API, Airbyte, Flink, Iceberg/Delta, dbt, ClickHouse,
IoT, SaaS Debezium, Spark S3/GCS/ADLS Spark SQL Pinot, Druid
Fivetran Streaming StarRocks
2020년 vs 2025년 주요 변화:
| 영역 | 2020 | 2025 |
|---|---|---|
| 스토리지 | Data Lake (원시) | Lakehouse (ACID) |
| 처리 | 배치 중심 | 스트림 우선 |
| 변환 | Stored Procedure | dbt + SQL |
| 오케스트레이션 | Airflow 1.x | Airflow 3.0 / Dagster |
| 포맷 | Parquet/ORC | Iceberg/Delta/Hudi |
| 카탈로그 | Hive Metastore | Unity Catalog / Polaris |
| 품질 | 수동 검증 | Great Expectations / Soda |
1.3 기술 스택 선택 프레임워크
데이터 스택 선택 시 고려해야 할 핵심 기준:
평가 기준 체크리스트:
1. 지연 시간 요구사항 (배치 vs 니어리얼타임 vs 실시간)
2. 데이터 볼륨 (GB/일 vs TB/일 vs PB/일)
3. 팀 규모와 역량 (SQL 중심 vs 코드 중심)
4. 벤더 종속성 허용 범위
5. 예산 (오픈소스 vs 매니지드 서비스)
6. 규제 요구사항 (데이터 거버넌스, 리니지)
2. 스트림 처리: Flink vs Spark vs Beam
2.1 스트림 처리가 중요한 이유
전통적인 배치 처리는 데이터를 모아서 한꺼번에 처리합니다. 하지만 현대 비즈니스는 즉각적인 인사이트를 요구합니다. 사기 탐지는 거래 발생 즉시 판단해야 하고, 추천 시스템은 사용자 행동에 실시간으로 반응해야 합니다.
스트림 처리의 핵심 개념:
[이벤트 스트림] ──→ [윈도우] ──→ [집계/변환] ──→ [싱크]
|
┌───────┼───────┐
│ │ │
Tumbling Sliding Session
Window Window Window
(고정) (슬라이딩) (세션)
2.2 Apache Flink: 진정한 이벤트 단위 처리
Flink는 이벤트 단위(event-by-event) 처리를 기본으로 하는 스트림 처리 엔진입니다. 배치는 스트림의 특수한 경우(bounded stream)로 취급합니다.
핵심 강점:
- 서브초(sub-second) 지연: 이벤트 발생 후 밀리초 내 처리
- 체크포인트 기반 상태 관리: 정확히 한 번(exactly-once) 보장
- 이벤트 시간 처리: 워터마크를 통한 정확한 시간 기반 처리
- Savepoint: 애플리케이션 업그레이드 시 상태 보존
// Flink - 실시간 사기 탐지 예제
DataStream<Transaction> transactions = env
.addSource(new FlinkKafkaConsumer<>("transactions", schema, props));
DataStream<Alert> alerts = transactions
.keyBy(Transaction::getAccountId)
.window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(1)))
.aggregate(new FraudScoreAggregator())
.filter(score -> score.getValue() > THRESHOLD);
alerts.addSink(new AlertSink());
Flink SQL — 스트림을 테이블처럼:
-- Flink SQL: 실시간 매출 집계
CREATE TABLE orders (
order_id STRING,
amount DECIMAL(10,2),
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'orders',
'format' = 'json'
);
SELECT
TUMBLE_START(order_time, INTERVAL '1' MINUTE) AS window_start,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue
FROM orders
GROUP BY TUMBLE(order_time, INTERVAL '1' MINUTE);
2.3 Apache Spark Structured Streaming: 배치+스트림 하이브리드
Spark는 마이크로 배치(micro-batching) 방식으로 스트림을 처리합니다. 배치 처리의 강점을 유지하면서 스트리밍을 지원합니다.
핵심 강점:
- 통합 API: 배치와 스트리밍에 동일한 DataFrame API 사용
- 방대한 생태계: MLlib, GraphX, SparkSQL과 원활한 통합
- 성숙한 커뮤니티: 가장 큰 사용자 기반과 문서
- Photon 엔진: Databricks의 C++ 네이티브 엔진으로 최대 12배 성능 향상
# Spark Structured Streaming - 실시간 주문 분석
from pyspark.sql import SparkSession
from pyspark.sql.functions import window, sum, count
spark = SparkSession.builder.appName("OrderAnalytics").getOrCreate()
orders = spark.readStream \
.format("kafka") \
.option("subscribe", "orders") \
.load()
order_stats = orders \
.withWatermark("order_time", "5 minutes") \
.groupBy(window("order_time", "1 minute")) \
.agg(
count("*").alias("order_count"),
sum("amount").alias("total_revenue")
)
order_stats.writeStream \
.format("iceberg") \
.outputMode("append") \
.option("checkpointLocation", "/checkpoint/orders") \
.toTable("catalog.db.order_stats")
2.4 Apache Beam: 통합 추상화 레이어
Beam은 파이프라인 정의와 실행 엔진을 분리하는 추상화 레이어입니다. 한 번 작성하면 Flink, Spark, Dataflow 등 다양한 러너에서 실행할 수 있습니다.
# Apache Beam - 멀티 러너 파이프라인
import apache_beam as beam
with beam.Pipeline(options=pipeline_options) as p:
(p
| 'ReadKafka' >> beam.io.ReadFromKafka(
consumer_config=kafka_config,
topics=['orders'])
| 'ParseJSON' >> beam.Map(parse_order)
| 'WindowInto' >> beam.WindowInto(
beam.window.FixedWindows(60)) # 1분 윈도우
| 'CountPerWindow' >> beam.combiners.Count.Globally()
| 'WriteToGCS' >> beam.io.WriteToText('gs://bucket/output'))
2.5 Flink vs Spark vs Beam 종합 비교
| 특성 | Apache Flink | Spark Structured Streaming | Apache Beam |
|---|---|---|---|
| 처리 모델 | 진정한 이벤트 단위 | 마이크로 배치 | 통합 API (러너 의존) |
| 지연 시간 | 밀리초 ~ 서브초 | 초 단위 (100ms~) | 러너에 따라 다름 |
| 상태 관리 | 내장 체크포인트, RocksDB | 제한적 (워터마크) | 러너에 따라 다름 |
| 정확도 | Exactly-once 기본 | Exactly-once 가능 | 러너 의존 |
| SQL 지원 | Flink SQL (강력) | Spark SQL (최고) | Beam SQL (제한적) |
| 배치 처리 | 좋음 (bounded stream) | 최고 (네이티브) | 좋음 |
| ML 통합 | 제한적 | MLlib / Spark ML | TFX 통합 |
| 학습 곡선 | 가파름 | 중간 | 가파름 |
| 주요 사용 사례 | 실시간 CEP, 사기 탐지 | 배치+스트림 하이브리드 | 멀티 엔진 이식성 |
| 대표 사용 기업 | Alibaba, Uber, Netflix | Databricks 고객사 전체 | Google Cloud 고객 |
선택 가이드:
- 밀리초 지연이 필수 → Flink
- 배치와 스트리밍 모두 중요 → Spark
- 멀티 클라우드 이식성 → Beam
- GCP 중심 → Beam + Dataflow
- Databricks 사용 중 → Spark
2.6 벤치마크: 실제 성능 비교
Nexocode의 2024 벤치마크 결과 (100만 이벤트/초 처리):
처리 지연 시간 (p99):
┌────────────────────────────────────────────┐
│ Flink ████ 23ms │
│ Spark ████████████████ 450ms │
│ Beam/Flink████ 25ms │
│ Beam/Spark████████████████ 460ms │
└────────────────────────────────────────────┘
초당 처리량 (이벤트/초):
┌────────────────────────────────────────────┐
│ Flink ████████████████████ 5.2M │
│ Spark ████████████████ 3.8M │
│ Beam/Flink██████████████████ 4.8M │
└────────────────────────────────────────────┘
3. 오케스트레이션: Airflow 3.0 vs Dagster vs Prefect vs Mage
3.1 오케스트레이션이란?
데이터 파이프라인 오케스트레이션은 작업의 순서, 의존성, 스케줄링, 재시도, 모니터링을 관리하는 것입니다. 현대 데이터 스택에서 오케스트레이터는 모든 구성 요소를 연결하는 "접착제" 역할을 합니다.
3.2 Apache Airflow 3.0: 왕좌의 진화
Airflow는 10년 이상 데이터 오케스트레이션의 사실상 표준이었습니다. 2025년 Airflow 3.0은 대대적인 혁신을 가져왔습니다.
Airflow 3.0 주요 변화:
Airflow 3.0 핵심 업그레이드:
1. React 기반 Modern UI
- 실시간 로그 스트리밍
- DAG 시각화 개선
- 다크 모드 지원
2. Event-Driven 스케줄링
- Dataset-aware 스케줄링 (데이터 도착 시 자동 트리거)
- External Trigger 강화
3. TaskFlow API 2.0
- 데코레이터 기반 Python 네이티브 DAG 작성
- 동적 태스크 매핑 개선
4. Edge Labels & DAG Versioning
- DAG 변경 이력 추적
- A/B 테스트 DAG 지원
5. 보안 강화
- RBAC 개선
- Secret Backend 통합 확장
# Airflow 3.0 - TaskFlow API 예제
from airflow.decorators import dag, task
from datetime import datetime
@dag(schedule="@daily", start_date=datetime(2025, 1, 1), catchup=False)
def data_pipeline():
@task()
def extract():
"""S3에서 원본 데이터 추출"""
import boto3
# 데이터 추출 로직
return {"records": 15000, "source": "s3://datalake/raw/"}
@task()
def transform(data: dict):
"""dbt를 통한 데이터 변환"""
import subprocess
subprocess.run(["dbt", "run", "--select", "staging+"])
return {"transformed": data["records"], "models": 12}
@task()
def load(data: dict):
"""ClickHouse에 결과 적재"""
# ClickHouse 적재 로직
return {"loaded": data["transformed"]}
@task()
def notify(result: dict):
"""Slack 알림 전송"""
# Slack 알림 로직
pass
raw = extract()
transformed = transform(raw)
loaded = load(transformed)
notify(loaded)
data_pipeline()
3.3 Dagster: 자산 중심 패러다임
Dagster는 "Software-Defined Assets" 개념을 중심으로 설계되었습니다. 태스크(task)가 아닌 **자산(asset)**을 중심으로 파이프라인을 정의합니다.
핵심 차별점:
- Asset 그래프: 데이터 자산 간 의존성을 명시적으로 관리
- dbt 네이티브 통합: dbt 프로젝트를 Dagster 자산으로 자동 변환
- 타입 시스템: I/O 매니저를 통한 강력한 타입 검증
- 관찰 가능성: 자산 구체화(materialization) 이력 자동 추적
# Dagster - Software-Defined Assets 예제
from dagster import asset, AssetExecutionContext
from dagster_dbt import DbtCliResource, dbt_assets
@asset(
description="원본 주문 데이터를 S3에서 추출",
group_name="raw",
compute_kind="python"
)
def raw_orders(context: AssetExecutionContext):
"""S3에서 주문 데이터 추출"""
import pandas as pd
df = pd.read_parquet("s3://datalake/raw/orders/")
context.log.info(f"Extracted {len(df)} orders")
return df
@asset(
description="주문 데이터 품질 검증",
group_name="validated",
compute_kind="great_expectations"
)
def validated_orders(raw_orders):
"""Great Expectations로 데이터 품질 검증"""
# 품질 검증 로직
assert raw_orders["amount"].min() >= 0, "음수 금액 발견"
return raw_orders
# dbt 자산 자동 통합
@dbt_assets(manifest=dbt_manifest_path)
def dbt_models(context: AssetExecutionContext, dbt: DbtCliResource):
yield from dbt.cli(["build"], context=context).stream()
3.4 Prefect: 가장 간단한 오케스트레이터
Prefect는 최소한의 보일러플레이트와 최고의 실패 처리를 목표로 합니다.
# Prefect - 간결한 파이프라인
from prefect import flow, task
from prefect.tasks import task_input_hash
from datetime import timedelta
@task(retries=3, retry_delay_seconds=60,
cache_key_fn=task_input_hash,
cache_expiration=timedelta(hours=1))
def extract_data(source: str) -> dict:
"""자동 재시도와 캐싱이 적용된 추출"""
# 데이터 추출
return {"data": [...], "count": 15000}
@task(log_prints=True)
def transform_data(raw: dict) -> dict:
"""변환 로직"""
print(f"Processing {raw['count']} records")
return {"transformed": raw["count"]}
@flow(name="daily-etl", log_prints=True)
def daily_pipeline():
raw = extract_data("s3://datalake/raw/")
result = transform_data(raw)
print(f"Pipeline complete: {result}")
# 실행
daily_pipeline()
3.5 Mage AI: 올인원 플랫폼
Mage는 데이터 엔지니어링의 **통합 개발 환경(IDE)**을 지향합니다. 노트북 스타일의 UI에서 파이프라인을 시각적으로 구축할 수 있습니다.
3.6 오케스트레이터 종합 비교
| 특성 | Airflow 3.0 | Dagster | Prefect | Mage |
|---|---|---|---|---|
| 패러다임 | DAG/태스크 중심 | 자산(Asset) 중심 | 플로우/태스크 | 노트북+파이프라인 |
| 학습 곡선 | 중간~높음 | 중간 | 낮음 | 낮음 |
| dbt 통합 | Provider 사용 | 네이티브 (최고) | 기본 지원 | 기본 지원 |
| UI | React (대폭 개선) | Dagit (우수) | Cloud UI | 노트북 스타일 |
| 스케일링 | KubernetesExecutor | K8s / ECS | Kubernetes | Kubernetes |
| 커뮤니티 | 최대 (10년+) | 빠르게 성장 중 | 중간 | 성장 중 |
| Provider 생태계 | 1000+ | 100+ | 200+ | 50+ |
| 실패 처리 | 중간 | 좋음 | 최고 | 좋음 |
| 가격 (Cloud) | MWAA 비용 | Dagster+ | Prefect Cloud | Mage Pro |
| 추천 시나리오 | 대규모/복잡한 파이프라인 | dbt 중심 프로젝트 | 빠른 시작, 간결함 | 탐색적 작업 |
최종 추천:
- 엔터프라이즈, 복잡한 파이프라인 → Airflow 3.0
- dbt 중심, 분석 엔지니어링 → Dagster
- 빠르게 시작, 간결함 우선 → Prefect
- 데이터 사이언스 + 엔지니어링 → Mage
4. dbt: SQL의 부활
4.1 dbt가 바꾼 데이터 변환의 패러다임
dbt(data build tool)는 ELT에서 "T(Transform)"를 담당하는 도구입니다. SQL만으로 데이터 변환 파이프라인을 구축할 수 있으며, 소프트웨어 엔지니어링 베스트 프랙티스(버전 관리, 테스트, 문서화)를 데이터 변환에 적용합니다.
dbt의 성장:
dbt 마일스톤:
- 2016: fishtown analytics 설립
- 2020: dbt Cloud 출시
- 2022: dbt Labs로 리브랜딩, Series D $222M
- 2023: MetricFlow 오픈소스화
- 2024: dbt Cloud에 Semantic Layer GA
- 2025: ARR $100M+ 돌파, de facto standard 지위 확립
4.2 dbt Core vs dbt Cloud
| 기능 | dbt Core (OSS) | dbt Cloud |
|---|---|---|
| 가격 | 무료 | Team $100/월~, Enterprise 커스텀 |
| 실행 환경 | CLI (로컬/CI) | 관리형 실행 환경 |
| 스케줄링 | 외부 (Airflow 등) | 내장 스케줄러 |
| IDE | VS Code + 확장 | Cloud IDE (브라우저) |
| Semantic Layer | MetricFlow CLI | 관리형 API |
| 문서 사이트 | 직접 호스팅 | 자동 생성/호스팅 |
| CI/CD | 직접 구성 | Slim CI 내장 |
4.3 실전 dbt 프로젝트 구조
dbt_project/
├── dbt_project.yml
├── packages.yml
├── profiles.yml
├── models/
│ ├── staging/ # 원본 데이터 정제
│ │ ├── stg_orders.sql
│ │ ├── stg_customers.sql
│ │ └── _staging.yml # 테스트 & 문서
│ ├── intermediate/ # 비즈니스 로직 조합
│ │ ├── int_order_items.sql
│ │ └── _intermediate.yml
│ ├── marts/ # 최종 분석용 테이블
│ │ ├── finance/
│ │ │ ├── fct_revenue.sql
│ │ │ └── dim_customers.sql
│ │ └── marketing/
│ │ └── fct_campaigns.sql
│ └── metrics/ # MetricFlow 메트릭
│ └── revenue.yml
├── tests/ # 커스텀 테스트
│ └── assert_positive_revenue.sql
├── macros/ # 재사용 가능한 SQL 매크로
│ └── cents_to_dollars.sql
├── seeds/ # 정적 데이터 (CSV)
│ └── country_codes.csv
└── snapshots/ # SCD Type 2 스냅샷
└── snap_customers.sql
핵심 모델 예제:
-- models/staging/stg_orders.sql
WITH source AS (
SELECT * FROM {{ source('raw', 'orders') }}
),
renamed AS (
SELECT
id AS order_id,
user_id AS customer_id,
status AS order_status,
amount_cents / 100.0 AS amount_dollars,
created_at AS ordered_at
FROM source
WHERE status != 'cancelled'
)
SELECT * FROM renamed
-- models/marts/finance/fct_revenue.sql
WITH orders AS (
SELECT * FROM {{ ref('stg_orders') }}
),
customers AS (
SELECT * FROM {{ ref('dim_customers') }}
),
final AS (
SELECT
o.order_id,
o.customer_id,
c.customer_segment,
o.amount_dollars,
o.ordered_at,
DATE_TRUNC('month', o.ordered_at) AS order_month
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_status = 'completed'
)
SELECT * FROM final
# models/staging/_staging.yml
version: 2
models:
- name: stg_orders
description: '정제된 주문 데이터'
columns:
- name: order_id
description: '주문 고유 식별자'
tests:
- unique
- not_null
- name: amount_dollars
description: '주문 금액 (달러)'
tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0
max_value: 100000
4.4 MetricFlow와 Semantic Layer
MetricFlow는 dbt Labs가 오픈소스로 공개한 메트릭 정의 프레임워크입니다. 비즈니스 메트릭을 한 곳에서 정의하면, 다양한 BI 도구에서 일관된 결과를 얻을 수 있습니다.
# models/metrics/revenue.yml
semantic_models:
- name: orders
defaults:
agg_time_dimension: ordered_at
model: ref('fct_revenue')
entities:
- name: order_id
type: primary
- name: customer_id
type: foreign
measures:
- name: revenue
agg: sum
expr: amount_dollars
- name: order_count
agg: count
expr: order_id
dimensions:
- name: ordered_at
type: time
- name: customer_segment
type: categorical
metrics:
- name: monthly_revenue
type: simple
type_params:
measure: revenue
- name: revenue_per_customer
type: derived
type_params:
expr: monthly_revenue / active_customers
metrics:
- name: monthly_revenue
- name: active_customers
4.5 dbt 베스트 프랙티스
dbt 프로젝트 베스트 프랙티스 10가지:
1. 계층 구조 엄수: staging → intermediate → marts
2. staging에서 모든 이름 변경 및 타입 캐스팅 완료
3. 모든 모델에 unique + not_null 테스트 적용
4. source freshness 체크로 데이터 신선도 모니터링
5. 증분 모델(incremental)로 대용량 테이블 최적화
6. packages.yml로 dbt-utils, dbt-expectations 활용
7. pre-commit 훅으로 SQL 린팅 (sqlfluff)
8. CI에서 slim CI (state:modified+) 활용
9. 환경별 프로파일 분리 (dev / staging / prod)
10. Semantic Layer로 메트릭 중앙 관리
5. 레이크하우스: Iceberg vs Delta Lake vs Hudi
5.1 레이크하우스가 필요한 이유
전통적으로 데이터 레이크와 데이터 웨어하우스는 별개의 시스템이었습니다. 데이터 레이크는 모든 형식의 데이터를 저렴하게 저장하지만 ACID 트랜잭션이 없었고, 데이터 웨어하우스는 빠른 분석 쿼리를 제공하지만 비용이 높았습니다.
레이크하우스는 이 둘을 결합합니다:
레이크하우스 = 데이터 레이크의 저비용 스토리지 + 웨어하우스의 ACID/성능
전통 아키텍처:
[Sources] → [Data Lake (S3)] → [ETL] → [Data Warehouse (Redshift)]
비용 높음, 중복 저장
레이크하우스 아키텍처:
[Sources] → [Object Storage (S3)] + [Table Format (Iceberg)]
저비용 ACID, 스키마 진화, 타임 트래블
└──→ [분석 엔진 (Spark/Trino/Flink)] 직접 쿼리
5.2 Apache Iceberg: 엔진 독립의 왕
Iceberg는 Netflix에서 개발한 오픈 테이블 포맷입니다. 특정 엔진에 종속되지 않으며, Spark, Flink, Trino, Presto, Hive 등 다양한 엔진에서 동일한 테이블에 접근할 수 있습니다.
핵심 기능:
- 스키마 진화(Schema Evolution): 칼럼 추가/삭제/이름변경 시 데이터 재작성 불필요
- 파티션 진화(Partition Evolution): 파티션 스키마 변경 시 기존 데이터 유지
- 타임 트래블: 특정 시점의 스냅샷으로 쿼리 가능
- 숨겨진 파티셔닝: 사용자가 파티션을 의식하지 않아도 자동 최적화
- 멀티 엔진: 어떤 엔진이든 동일한 테이블 포맷 사용
-- Iceberg 테이블 생성 및 활용
CREATE TABLE catalog.db.orders (
order_id BIGINT,
customer_id BIGINT,
amount DECIMAL(10,2),
status STRING,
ordered_at TIMESTAMP
)
USING iceberg
PARTITIONED BY (days(ordered_at));
-- 타임 트래블: 어제 시점의 데이터 조회
SELECT * FROM catalog.db.orders
VERSION AS OF '2025-03-21T00:00:00';
-- 스냅샷 기반 증분 읽기
SELECT * FROM catalog.db.orders
WHERE ordered_at > (
SELECT max(ordered_at)
FROM catalog.db.orders
VERSION AS OF 'snapshot_id_123'
);
Tabular 인수 (2024): Databricks가 Iceberg의 상용화 기업 Tabular를 인수했습니다. 이로 인해 Iceberg의 상업적 생태계에 중대한 변화가 발생했으며, Snowflake와 기타 벤더는 독자적인 Iceberg 지원을 강화하고 있습니다.
5.3 Delta Lake: Databricks 생태계의 핵심
Delta Lake는 Databricks에서 개발한 오픈소스 테이블 포맷입니다. Spark와의 깊은 통합이 강점입니다.
핵심 기능:
- UniForm: Iceberg와 Hudi 호환 메타데이터 자동 생성
- Liquid Clustering: 기존 Z-Order를 대체하는 자동 데이터 레이아웃 최적화
- Change Data Feed: CDC 이벤트를 Delta 테이블에서 직접 캡처
- Deletion Vectors: 효율적인 삭제/업데이트 (파일 재작성 없이)
# Delta Lake - UniForm으로 Iceberg 호환
from delta.tables import DeltaTable
# Delta 테이블 생성 (UniForm 활성화)
spark.sql("""
CREATE TABLE catalog.db.events (
event_id BIGINT,
event_type STRING,
payload STRING,
event_time TIMESTAMP
)
USING DELTA
TBLPROPERTIES (
'delta.universalFormat.enabledFormats' = 'iceberg'
)
""")
# Delta 테이블에 MERGE (Upsert)
delta_table = DeltaTable.forName(spark, "catalog.db.events")
delta_table.alias("target").merge(
new_events.alias("source"),
"target.event_id = source.event_id"
).whenMatchedUpdateAll() \
.whenNotMatchedInsertAll() \
.execute()
5.4 Apache Hudi: 실시간 CDC의 강자
Hudi(Hadoop Upserts Deletes and Incrementals)는 Uber에서 개발한 테이블 포맷으로, 레코드 수준 인덱싱과 실시간 CDC 처리에 최적화되어 있습니다.
핵심 기능:
- 레코드 레벨 인덱스: 개별 레코드 업서트가 매우 빠름
- Copy-on-Write vs Merge-on-Read: 워크로드에 따른 최적 전략 선택
- Incremental Query: 변경된 레코드만 효율적으로 읽기
- Concurrency Control: 낙관적 동시성 제어
5.5 Iceberg vs Delta Lake vs Hudi 종합 비교
| 특성 | Apache Iceberg | Delta Lake | Apache Hudi |
|---|---|---|---|
| 개발사 | Netflix (현재 Apache) | Databricks | Uber (현재 Apache) |
| 최적 사용 사례 | 멀티 엔진, 페타바이트 | Databricks 생태계 | 실시간 CDC/Upsert |
| 엔진 지원 | Spark, Flink, Trino, Presto | Spark (최적), 기타 제한적 | Spark, Flink |
| 스키마 진화 | 최고 (full evolution) | 좋음 | 좋음 |
| 파티션 진화 | 최고 (hidden partitioning) | Liquid Clustering | 제한적 |
| CDC 지원 | 증분 읽기만 | Change Data Feed | 네이티브 (최고) |
| 타임 트래블 | 스냅샷 기반 | 버전 기반 | 커밋 타임라인 |
| 동시성 | Optimistic (branch 지원) | Optimistic | Optimistic |
| 호환성 | 표준 (오픈) | UniForm (Iceberg 호환) | 제한적 |
| 카탈로그 | Polaris, REST, Hive | Unity Catalog | 자체 타임라인 |
| 커뮤니티 규모 | 빠르게 성장 (최대) | Databricks 주도 | 중간 |
선택 가이드:
- 멀티 엔진, 벤더 독립 → Iceberg
- Databricks 사용 중 → Delta Lake (UniForm으로 Iceberg 호환 확보)
- 실시간 CDC, 빈번한 업서트 → Hudi
- 신규 프로젝트, 최대 유연성 → Iceberg
6. 실시간 분석: ClickHouse vs Druid vs Pinot vs StarRocks
6.1 OLAP 엔진이 필요한 이유
레이크하우스에 데이터를 저장했다면, 이제 밀리초 단위로 분석 쿼리를 실행할 수 있는 OLAP 엔진이 필요합니다. PostgreSQL이나 MySQL로는 수십억 행 규모의 집계 쿼리를 실시간으로 처리할 수 없습니다.
6.2 ClickHouse: 가장 빠른 오픈소스 OLAP
ClickHouse는 Yandex에서 개발한 칼럼 기반 OLAP 데이터베이스입니다. 단일 노드에서도 초당 수십억 행을 스캔할 수 있습니다.
-- ClickHouse - 실시간 대시보드 쿼리
CREATE TABLE events (
event_id UInt64,
user_id UInt32,
event_type LowCardinality(String),
properties Map(String, String),
event_time DateTime64(3)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_type, user_id, event_time);
-- Materialized View로 실시간 집계
CREATE MATERIALIZED VIEW hourly_events_mv
ENGINE = SummingMergeTree()
ORDER BY (event_type, hour)
AS SELECT
event_type,
toStartOfHour(event_time) AS hour,
count() AS event_count,
uniqHLL12(user_id) AS unique_users
FROM events
GROUP BY event_type, hour;
-- 수십억 행에서 밀리초 응답
SELECT
event_type,
sum(event_count) AS total_events,
sum(unique_users) AS total_users
FROM hourly_events_mv
WHERE hour >= now() - INTERVAL 24 HOUR
GROUP BY event_type
ORDER BY total_events DESC
LIMIT 20;
6.3 Apache Druid: 실시간 수집 + 분석
Druid는 실시간 데이터 수집과 분석을 동시에 처리하도록 설계된 OLAP 데이터베이스입니다. Kafka에서 직접 데이터를 수집하면서 동시에 쿼리를 처리할 수 있습니다.
6.4 Apache Pinot: LinkedIn의 실시간 분석
Pinot은 LinkedIn에서 개발한 OLAP 데이터베이스로, 사용자 대면(user-facing) 분석에 최적화되어 있습니다. LinkedIn의 "Who viewed your profile", Uber의 UberEats 레스토랑 대시보드 등에 사용됩니다.
6.5 StarRocks: 차세대 OLAP의 도전장
StarRocks는 MySQL 호환 프로토콜을 지원하는 차세대 OLAP 엔진입니다. 별도의 데이터 적재 없이 Iceberg, Hudi, Delta Lake 테이블을 직접 쿼리할 수 있는 레이크하우스 분석 기능이 독특합니다.
6.6 OLAP 엔진 종합 비교
| 특성 | ClickHouse | Apache Druid | Apache Pinot | StarRocks |
|---|---|---|---|---|
| 개발사 | Yandex → ClickHouse Inc | Imply | LinkedIn → StarTree | CelerData |
| 아키텍처 | 단일 바이너리 | MSQ (복잡) | Helix 기반 | MPP (단순) |
| 실시간 수집 | Kafka 엔진 | 네이티브 (최고) | 네이티브 (좋음) | Routine Load |
| 쿼리 성능 | 최고 (단일노드) | 좋음 | 좋음 | 최고 (MPP) |
| SQL 호환 | ANSI SQL | Druid SQL | PQL + SQL | MySQL 호환 |
| 조인 성능 | 제한적 | 제한적 | 제한적 | 좋음 (MPP) |
| 레이크하우스 쿼리 | 제한적 | 제한적 | 제한적 | 네이티브 (최고) |
| 운영 복잡도 | 낮음 | 높음 | 중간 | 낮음 |
| 학습 곡선 | 낮음 | 높음 | 중간 | 낮음 (MySQL) |
| 스케일링 | 수평 확장 | 수평 확장 | 수평 확장 | 수평 확장 |
벤치마크 (ClickBench 2024):
TPC-H 10GB 쿼리 성능 (초, 낮을수록 좋음):
┌──────────────────────────────────────────────────┐
│ StarRocks 3.x ████ 2.1s (2.2x faster than CH) │
│ ClickHouse █████████ 4.6s │
│ Druid ████████████████████ 18.7s │
│ Pinot ██████████████████ 16.3s │
└──────────────────────────────────────────────────┘
선택 가이드:
- 최고 단일노드 성능, 간단한 운영 → ClickHouse
- Kafka 네이티브 실시간 수집 → Druid
- 사용자 대면 분석, 대규모 → Pinot
- 레이크하우스 직접 쿼리, MySQL 호환 → StarRocks
7. Data Mesh와 Data Contracts
7.1 Data Mesh: 분산형 데이터 아키텍처
Data Mesh는 Zhamak Dehghani가 제안한 분산형 데이터 아키텍처 패러다임입니다. 중앙 데이터 팀이 모든 데이터를 관리하는 대신, 도메인 팀이 자신의 데이터 제품(Data Product)을 소유하고 관리합니다.
Data Mesh의 4대 원칙:
1. 도메인 소유권 (Domain Ownership)
→ 각 도메인 팀이 자신의 데이터를 소유
2. 제품으로서의 데이터 (Data as a Product)
→ 데이터에 SLA, 문서, 품질 보증 적용
3. 셀프 서비스 플랫폼 (Self-Serve Platform)
→ 인프라 팀이 데이터 플랫폼 도구 제공
4. 연합 거버넌스 (Federated Governance)
→ 전사 표준 + 도메인 자율성의 균형
Gartner 2025 전망: 대기업의 70%가 Data Mesh 파일럿을 진행 중이거나 계획하고 있습니다. 하지만 완전한 Data Mesh 전환에 성공한 사례는 아직 소수입니다.
7.2 Data Contracts: 데이터 API의 약속
Data Contract는 데이터 생산자와 소비자 사이의 공식적인 합의입니다. API 스펙처럼, 데이터의 스키마, 품질, SLA를 명시합니다.
# data-contract.yml 예시
dataContractSpecification: 0.9.3
id: orders-contract
info:
title: '주문 데이터 계약'
version: 2.1.0
owner: order-domain-team
contact:
name: '주문팀'
email: orders@company.com
servers:
production:
type: iceberg
catalog: polaris
database: orders_db
table: orders
models:
orders:
description: '완료된 주문 데이터'
fields:
order_id:
type: bigint
required: true
unique: true
description: '주문 고유 식별자'
customer_id:
type: bigint
required: true
amount:
type: decimal
required: true
minimum: 0
description: '주문 금액 (USD)'
ordered_at:
type: timestamp
required: true
quality:
type: SodaCL
specification:
checks for orders:
- row_count > 0
- missing_count(order_id) = 0
- duplicate_count(order_id) = 0
- avg(amount) between 10 and 500
sla:
freshness: 1 hour
availability: 99.9%
latency: p99 < 500ms
7.3 Data Mesh + Data Fabric 하이브리드
2025년의 현실적인 트렌드는 Data Mesh와 Data Fabric의 하이브리드 접근입니다.
Data Mesh + Fabric 하이브리드:
┌─────────────────────┐
│ Data Fabric Layer │
│ (통합 거버넌스/카탈로그)│
└────────┬────────────┘
┌────────────────┼────────────────┐
│ │ │
┌──────┴──────┐ ┌─────┴──────┐ ┌──────┴──────┐
│ 주문 도메인 │ │ 고객 도메인 │ │ 상품 도메인 │
│ Data Product │ │ Data Product│ │ Data Product │
│ (Iceberg) │ │ (Iceberg) │ │ (Iceberg) │
└─────────────┘ └────────────┘ └─────────────┘
도구 생태계:
- 카탈로그: OpenMetadata, DataHub, Amundsen
- 품질: Great Expectations, Soda, Montecarlo
- 계약: Datakin, Schemata, Bitol
- 리니지: Marquez, OpenLineage
8. 실전 아키텍처: 실시간 추천 시스템
8.1 요구사항
대규모 이커머스 플랫폼의 실시간 개인화 추천 시스템을 구축한다고 가정합니다.
요구사항:
- 일일 활성 사용자: 500만명
- 초당 이벤트: 50,000건
- 추천 지연 시간: p99 < 100ms
- 데이터 소스: 클릭 스트림, 구매, 검색, 재고
- 피처 스토어: 실시간 + 배치 피처 결합
- A/B 테스트: 모델 버전별 성과 비교
8.2 아키텍처 설계
실시간 추천 시스템 아키텍처:
[클릭/구매/검색 이벤트]
│
▼
┌─────────┐ ┌──────────────┐
│ Kafka │────→│ Flink (CEP) │──→ 실시간 피처 계산
│ Cluster │ │ 윈도우 집계 │ (최근 5분 클릭 카테고리)
└────┬────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────┐ ┌──────────────┐
│ Iceberg │ │ Redis │──→ 실시간 피처 서빙
│ (원본) │ │ Feature Store│ (p99 < 5ms)
└────┬────┘ └──────────────┘
│ ↑
▼ │
┌─────────┐ ┌──────────────┐
│ dbt │────→│ ClickHouse │──→ 배치 피처 계산
│ (변환) │ │ (집계/분석) │ (30일 구매 패턴)
└────┬────┘ └──────────────┘
│
▼
┌─────────────────┐ ┌──────────────┐
│ ML Model Serving │←───│ Feature Store │
│ (TorchServe/ │ │ (Redis+DuckDB)│
│ Triton) │ └──────────────┘
└────────┬────────┘
│
▼
┌─────────────────┐
│ API Gateway │──→ 사용자에게 추천 결과 제공
│ (p99 < 100ms) │
└─────────────────┘
8.3 구현 핵심 코드
Flink 실시간 피처 계산:
// 실시간 사용자 행동 피처 계산
DataStream<UserFeature> realtimeFeatures = clickEvents
.keyBy(ClickEvent::getUserId)
.window(SlidingEventTimeWindows.of(
Time.minutes(5), Time.minutes(1)))
.aggregate(new UserBehaviorAggregator());
// Redis 피처 스토어에 저장
realtimeFeatures.addSink(
new RedisSink<>(redisConfig, new FeatureRedisMapper()));
dbt 배치 피처 모델:
-- models/features/user_purchase_patterns.sql
-- 30일 구매 패턴 피처 (배치)
WITH purchase_stats AS (
SELECT
customer_id,
COUNT(*) AS purchase_count_30d,
SUM(amount_dollars) AS total_spend_30d,
AVG(amount_dollars) AS avg_order_value_30d,
COUNT(DISTINCT category) AS unique_categories_30d,
MAX(ordered_at) AS last_purchase_at
FROM {{ ref('fct_revenue') }}
WHERE ordered_at >= CURRENT_DATE - INTERVAL '30' DAY
GROUP BY customer_id
)
SELECT
customer_id,
purchase_count_30d,
total_spend_30d,
avg_order_value_30d,
unique_categories_30d,
DATEDIFF('day', last_purchase_at, CURRENT_DATE) AS days_since_last_purchase
FROM purchase_stats
Airflow DAG:
# 추천 시스템 일일 파이프라인
@dag(schedule="0 2 * * *", catchup=False)
def recommendation_pipeline():
@task()
def run_dbt_features():
"""dbt로 배치 피처 생성"""
import subprocess
result = subprocess.run(
["dbt", "run", "--select", "features+"],
capture_output=True, text=True
)
return result.returncode == 0
@task()
def export_to_feature_store(dbt_success: bool):
"""배치 피처를 Feature Store로 내보내기"""
if not dbt_success:
raise Exception("dbt 실행 실패")
# ClickHouse → Redis 피처 스토어 동기화
pass
@task()
def retrain_model():
"""ML 모델 재훈련"""
# 일일 모델 재훈련 로직
pass
@task()
def validate_model(retrain_result):
"""모델 성능 검증"""
# A/B 테스트 메트릭 비교
pass
dbt_result = run_dbt_features()
export = export_to_feature_store(dbt_result)
retrain = retrain_model()
validate_model(retrain)
recommendation_pipeline()
8.4 비용 최적화
월간 예상 비용 (AWS 기준, 일일 500만 유저):
| 구성 요소 | 인스턴스 타입 | 월 비용 |
|---------------|--------------------|----------- |
| Kafka (MSK) | kafka.m5.2xlarge x3| 약 2,100달러 |
| Flink (EMR) | m5.2xlarge x4 | 약 2,800달러 |
| Iceberg (S3) | 50TB 스토리지 | 약 1,150달러 |
| ClickHouse | m5.4xlarge x3 | 약 4,200달러 |
| Redis | r6g.2xlarge x2 | 약 1,800달러 |
| Airflow (MWAA)| mw1.medium | 약 350달러 |
총 예상 비용: 약 12,400달러/월
비용 최적화 전략:
1. Spot 인스턴스 활용 (Flink 워커) → 60% 절감
2. Iceberg 컴팩션 최적화 → 스토리지 30% 절감
3. ClickHouse TTL 정책 → 오래된 데이터 자동 삭제
4. Reserved Instance → 장기 워크로드 40% 절감
9. 자격증과 학습 로드맵
9.1 2025년 추천 자격증
| 자격증 | 제공사 | 난이도 | 핵심 내용 | 추천 대상 |
|---|---|---|---|---|
| DP-700 | Microsoft | 중간 | Fabric Analytics Engineer | Azure 생태계 |
| Data Engineer Associate | Databricks | 중간 | Spark, Delta Lake, Unity | Databricks 사용자 |
| Professional DE | Google Cloud | 높음 | BigQuery, Dataflow, GCS | GCP 생태계 |
| Data Engineer | AWS (DEA-C01) | 중간 | Glue, Redshift, EMR | AWS 생태계 |
| dbt Analytics Engineering | dbt Labs | 낮음~중간 | dbt Core, 모델링 | 분석 엔지니어 |
| CCA Spark and Hadoop | Cloudera | 중간 | Spark, HDFS, Hive | 온프레미스 |
9.2 12개월 학습 로드맵
12개월 데이터 엔지니어 학습 경로:
[1-3개월: 기초]
├── SQL 고급 (윈도우 함수, CTE, 최적화)
├── Python 데이터 처리 (pandas, polars)
├── Linux / Docker 기초
└── Git / CI/CD 기초
[4-6개월: 핵심 도구]
├── Apache Spark (DataFrame API, SparkSQL)
├── dbt (프로젝트 구조, 테스트, 문서화)
├── Airflow (DAG 작성, 오퍼레이터)
└── Kafka 기초 (프로듀서, 컨슈머, 토픽)
[7-9개월: 심화]
├── Apache Flink (스트림 처리, 상태 관리)
├── Iceberg / Delta Lake (레이크하우스)
├── ClickHouse (OLAP 분석)
└── Great Expectations (데이터 품질)
[10-12개월: 실전 & 자격증]
├── 포트폴리오 프로젝트 3개 완성
├── 자격증 1-2개 취득
├── Data Mesh / 거버넌스 학습
└── 기술 블로그 작성 시작
9.3 추천 학습 리소스
무료 리소스:
- DataTalksClub DE Zoomcamp: 무료 온라인 부트캠프 (16주)
- Databricks Academy: Spark, Delta Lake 무료 과정
- dbt Learn: 공식 dbt 튜토리얼
- ClickHouse University: 공식 무료 과정
유료 리소스:
- Zach Wilson의 Data Engineering Bootcamp: 실전 프로젝트 중심
- O'Reilly "Fundamentals of Data Engineering": 바이블
- Udemy "Apache Flink 마스터 클래스": Flink 심화
10. 포트폴리오 프로젝트 3개
프로젝트 1: 실시간 택시 요금 예측 파이프라인
기술 스택: Kafka + Flink + Iceberg + dbt + ClickHouse
아키텍처:
NYC 택시 데이터(API) → Kafka → Flink(실시간 피처) → Iceberg
↓
dbt(배치 변환)
↓
ClickHouse(대시보드)
구현 포인트:
1. Flink로 실시간 지역별 수요 예측 피처 생성
2. Iceberg 파티션 진화 활용 (일별 → 시간별)
3. dbt 증분 모델로 일일 집계
4. ClickHouse Materialized View로 실시간 대시보드
5. Airflow DAG로 전체 파이프라인 오케스트레이션
프로젝트 2: E-Commerce CDC 파이프라인
기술 스택: Debezium + Kafka + Spark + Delta Lake + dbt
아키텍처:
PostgreSQL(OLTP) → Debezium(CDC) → Kafka → Spark Streaming → Delta Lake
↓
dbt(계층적 변환)
↓
Superset(BI 대시보드)
구현 포인트:
1. Debezium으로 PostgreSQL 변경 사항 실시간 캡처
2. Spark Structured Streaming으로 CDC 이벤트 처리
3. Delta Lake MERGE로 SCD Type 2 구현
4. dbt로 staging/intermediate/marts 구조화
5. Superset으로 매출/재고 실시간 대시보드
프로젝트 3: Data Contract 기반 Data Mesh
기술 스택: OpenMetadata + Soda + dbt + Iceberg + Dagster
아키텍처:
도메인 A(주문) ──→ Data Contract(YAML) ──→ Iceberg
도메인 B(고객) ──→ Data Contract(YAML) ──→ Iceberg
도메인 C(상품) ──→ Data Contract(YAML) ──→ Iceberg
↓
OpenMetadata(카탈로그/리니지)
Soda(품질 검증)
Dagster(오케스트레이션)
구현 포인트:
1. 3개 도메인의 Data Contract YAML 정의
2. Soda로 자동 데이터 품질 검증
3. OpenMetadata로 리니지와 카탈로그 관리
4. Dagster Software-Defined Assets로 파이프라인 구축
5. 전사 메트릭 레이어 (MetricFlow)
퀴즈
퀴즈 1: 스트림 처리 엔진 선택
Q: 신용카드 사기 탐지 시스템을 구축해야 합니다. 트랜잭션 발생 후 50ms 내에 판단해야 하며, 최근 10분간의 거래 패턴을 분석해야 합니다. 어떤 스트림 처리 엔진이 가장 적합할까요?
A: Apache Flink
Flink가 최적인 이유:
- 이벤트 단위 처리: Flink는 진정한 이벤트별 처리를 하므로, 트랜잭션 하나하나를 즉시 처리할 수 있습니다.
- 서브밀리초 지연: Spark의 마이크로 배치(최소 100ms+)와 달리, Flink는 밀리초 단위 지연을 보장합니다.
- 체크포인트 기반 상태 관리: 10분 슬라이딩 윈도우의 상태를 RocksDB에 안정적으로 유지하며, 정확히 한 번(exactly-once) 의미를 보장합니다.
- CEP(Complex Event Processing): Flink CEP 라이브러리로 복잡한 사기 패턴을 선언적으로 정의할 수 있습니다.
Spark는 마이크로 배치 특성상 50ms 제약을 충족하기 어렵고, Beam은 러너에 따라 성능이 달라지므로 이 시나리오에서는 Flink가 최선입니다.
퀴즈 2: 레이크하우스 포맷 선택
Q: 회사에서 Spark, Trino, Flink 세 가지 엔진을 모두 사용하고 있으며, 매일 수백 GB의 CDC 데이터를 처리합니다. 스키마가 자주 변경되는 상황에서 어떤 테이블 포맷을 선택해야 할까요?
A: Apache Iceberg
Iceberg가 최적인 이유:
- 멀티 엔진 지원: Spark, Trino, Flink 모두 Iceberg를 1등급(first-class)으로 지원합니다. Delta Lake는 Spark 외 엔진 지원이 제한적이고, Hudi는 Trino 지원이 약합니다.
- 스키마 진화: Iceberg는 full schema evolution을 지원합니다. 칼럼 추가, 삭제, 이름 변경, 타입 변경 시 기존 데이터를 재작성할 필요가 없습니다.
- 파티션 진화: Hidden partitioning으로 파티션 스키마가 변경되어도 기존 데이터와 쿼리에 영향이 없습니다.
단, CDC 처리가 매우 빈번한 레코드 수준 업서트가 핵심이라면 Hudi를 검토할 수 있지만, 멀티 엔진과 스키마 진화를 우선시한다면 Iceberg가 최선의 선택입니다.
퀴즈 3: dbt 프로젝트 구조
Q: dbt 프로젝트에서 staging 모델과 marts 모델의 차이점은 무엇이며, 왜 이런 계층 구조가 필요할까요?
A:
staging 모델:
- 원본 데이터(source)를 1:1로 매핑하여 정제하는 레이어
- 칼럼 이름 변경, 타입 캐스팅, 필터링 등 기본 변환만 수행
- 비즈니스 로직을 포함하지 않음
- 소스당 하나의 staging 모델 생성
marts 모델:
- 비즈니스 질문에 답하는 최종 분석용 테이블
- 여러 staging/intermediate 모델을 조인하여 비즈니스 로직 적용
- fact(팩트)와 dimension(디멘전) 테이블로 구분
- 비즈니스 도메인별로 그룹화 (finance, marketing 등)
계층 구조가 필요한 이유:
- DRY 원칙: 소스 변경 시 staging만 수정하면 하위 모든 모델에 반영
- 테스트 용이성: 각 레이어별로 독립적인 테스트 가능
- 디버깅: 문제 발생 시 어느 레이어에서 발생했는지 빠르게 파악
- 팀 협업: 분석가는 marts만, 엔지니어는 staging을 관리
퀴즈 4: 오케스트레이터 선택
Q: 5명 규모의 분석 엔지니어링 팀에서, dbt 중심의 데이터 파이프라인을 운영하려 합니다. 기존에 Airflow 경험이 없으며, 최대한 빨리 운영 환경을 구축해야 합니다. 어떤 오케스트레이터를 추천하시나요?
A: Dagster
Dagster가 최적인 이유:
- dbt 네이티브 통합: Dagster는 dbt 프로젝트를 자동으로 Software-Defined Assets로 변환합니다. dbt 모델 간 의존성이 Dagster 자산 그래프에 자동 반영됩니다.
- 낮은 학습 곡선 (dbt 사용자 기준): dbt 사용자에게 Dagster의 자산 중심 패러다임이 자연스럽습니다. Airflow의 DAG/Operator 개념보다 직관적입니다.
- 빠른 운영 환경 구축: Dagster Cloud를 사용하면 인프라 관리 없이 바로 시작할 수 있습니다.
- 관찰 가능성: 자산 구체화(materialization) 이력, 데이터 리니지, 메타데이터를 자동으로 추적합니다.
Airflow는 경험 없는 팀에게 초기 설정과 운영이 복잡하고, Prefect는 dbt 통합이 Dagster만큼 깊지 않습니다.
퀴즈 5: OLAP 엔진 선택
Q: 이미 Iceberg 레이크하우스를 구축했으며, 별도의 데이터 적재 없이 Iceberg 테이블에 직접 분석 쿼리를 실행하고 싶습니다. 기존 팀이 MySQL에 익숙합니다. 어떤 OLAP 엔진이 가장 적합할까요?
A: StarRocks
StarRocks가 최적인 이유:
- 레이크하우스 네이티브 쿼리: StarRocks는 Iceberg, Delta Lake, Hudi 테이블에 직접 쿼리할 수 있습니다. 데이터를 별도로 적재(ingestion)할 필요가 없어 데이터 중복과 동기화 비용을 절감합니다.
- MySQL 호환 프로토콜: MySQL 클라이언트와 드라이버를 그대로 사용할 수 있어 MySQL에 익숙한 팀이 바로 적응할 수 있습니다.
- MPP 아키텍처: 대규모 조인 쿼리에서 ClickHouse보다 우수한 성능을 보입니다.
- 간단한 운영: 단순한 아키텍처로 Druid 대비 운영이 훨씬 쉽습니다.
ClickHouse는 Iceberg 직접 쿼리 지원이 제한적이고, Druid/Pinot은 운영 복잡도가 높으며 MySQL 호환이 아닙니다.
참고 자료
공식 문서
- Apache Flink Documentation — Flink 공식 문서
- Apache Spark Documentation — Spark 공식 문서
- dbt Documentation — dbt 공식 문서
- Apache Iceberg Documentation — Iceberg 공식 문서
- ClickHouse Documentation — ClickHouse 공식 문서
- Apache Airflow Documentation — Airflow 공식 문서
- Dagster Documentation — Dagster 공식 문서
아키텍처 및 비교
- Nexocode: Flink vs Spark vs Beam Benchmark 2024 — 스트림 처리 엔진 벤치마크
- Databricks: Delta Lake UniForm — Delta UniForm 공식 문서
- ClickBench: OLAP Database Benchmarks — OLAP 성능 비교
- Data Mesh Architecture by Zhamak Dehghani — Data Mesh 원칙과 아키텍처
학습 리소스
- DataTalksClub Data Engineering Zoomcamp — 무료 부트캠프
- O'Reilly: Fundamentals of Data Engineering — 데이터 엔지니어링 바이블
- dbt Learn — dbt 공식 학습 과정
- Databricks Academy — Spark/Delta 무료 과정
- Apache Iceberg: The Definitive Guide (O'Reilly) — Iceberg 심화 학습
트렌드 및 분석
- Gartner: Modern Data Stack Trends 2025 — 데이터 엔지니어링 트렌드
- [a]16z: The Modern Data Stack (2024 Update)](https://a16z.com/) — VC 관점의 데이터 스택 분석
Data Engineering Complete Guide 2025: Flink vs Spark, dbt, Iceberg, Airflow — Modern Data Stack Deep Dive
- Introduction
- 1. The 2025 Data Engineering Landscape
- 2. Stream Processing: Flink vs Spark vs Beam
- 3. Orchestration: Airflow 3.0 vs Dagster vs Prefect vs Mage
- 4. dbt: The SQL Renaissance
- 5. Lakehouse: Iceberg vs Delta Lake vs Hudi
- 6. Real-Time Analytics: ClickHouse vs Druid vs Pinot vs StarRocks
- 7. Data Mesh and Data Contracts
- 8. Production Architecture: Real-Time Recommendation System
- 9. Certifications and Learning Roadmap
- 10. Three Portfolio Projects
- Quizzes
- References
Introduction
In 2025, data engineering has become one of the most sought-after roles in the software industry. With the explosion of AI/ML pipeline demand, the role has expanded from simple ETL development to data platform architecture. According to LinkedIn's 2025 Jobs Report, data engineer demand grew 35% year-over-year, with a notable salary premium for engineers experienced in streaming processing and lakehouse technologies.
This article comprehensively covers every core component of the Modern Data Stack. From the Flink vs Spark stream processing comparison, to dbt's SQL revolution, the Iceberg lakehouse wars, ClickHouse real-time analytics, and Airflow 3.0 orchestration — we cover everything about data engineering in 2025, complete with production architectures.
1. The 2025 Data Engineering Landscape
1.1 Why Data Engineers Are the Hottest Role
Three reasons why data engineers are in such high demand in 2025:
First, AI/ML pipeline demand is exploding. Every company is trying to adopt AI post-ChatGPT, but there's an absolute shortage of talent who can build high-quality data pipelines for model training. According to Gartner estimates, 85% of ML projects fail due to data quality issues.
Second, real-time processing requirements are surging. Batch processing alone is no longer sufficient. Use cases requiring millisecond-level decisions — fraud detection, real-time recommendations, dynamic pricing — are growing explosively.
Third, regulatory and data governance needs. With GDPR, the AI Act, and other data regulations tightening, the value of engineers who can professionally handle data lineage and quality management has increased.
1.2 Evolution of the Modern Data Stack
The modern data stack has evolved into the following layered structure:
[Data Sources] → [Ingestion/CDC] → [Stream Processing] → [Storage/Lakehouse] → [Transform] → [Analytics/Serving]
| | | | | |
DB, API, Airbyte, Flink, Iceberg/Delta, dbt, ClickHouse,
IoT, SaaS Debezium, Spark S3/GCS/ADLS Spark SQL Pinot, Druid
Fivetran Streaming StarRocks
2020 vs 2025 key changes:
| Area | 2020 | 2025 |
|---|---|---|
| Storage | Data Lake (raw) | Lakehouse (ACID) |
| Processing | Batch-first | Stream-first |
| Transformation | Stored Procedures | dbt + SQL |
| Orchestration | Airflow 1.x | Airflow 3.0 / Dagster |
| Format | Parquet/ORC | Iceberg/Delta/Hudi |
| Catalog | Hive Metastore | Unity Catalog / Polaris |
| Quality | Manual validation | Great Expectations / Soda |
1.3 Technology Stack Selection Framework
Key criteria to consider when choosing a data stack:
Evaluation Criteria Checklist:
1. Latency requirements (batch vs near-real-time vs real-time)
2. Data volume (GB/day vs TB/day vs PB/day)
3. Team size and capabilities (SQL-centric vs code-centric)
4. Vendor lock-in tolerance
5. Budget (open source vs managed services)
6. Regulatory requirements (data governance, lineage)
2. Stream Processing: Flink vs Spark vs Beam
2.1 Why Stream Processing Matters
Traditional batch processing collects data and processes it all at once. But modern businesses demand instant insights. Fraud detection must be instantaneous, and recommendation systems need to respond to user behavior in real-time.
Core concepts of stream processing:
[Event Stream] ──→ [Window] ──→ [Aggregate/Transform] ──→ [Sink]
|
┌───────┼───────┐
│ │ │
Tumbling Sliding Session
Window Window Window
(fixed) (sliding) (session)
2.2 Apache Flink: True Event-by-Event Processing
Flink is a stream processing engine with event-by-event processing as its default. Batch is treated as a special case of streaming (bounded stream).
Key strengths:
- Sub-second latency: Processing within milliseconds of event occurrence
- Checkpoint-based state management: Exactly-once guarantees
- Event-time processing: Accurate time-based processing via watermarks
- Savepoints: State preservation during application upgrades
// Flink - Real-time fraud detection example
DataStream<Transaction> transactions = env
.addSource(new FlinkKafkaConsumer<>("transactions", schema, props));
DataStream<Alert> alerts = transactions
.keyBy(Transaction::getAccountId)
.window(SlidingEventTimeWindows.of(Time.minutes(10), Time.minutes(1)))
.aggregate(new FraudScoreAggregator())
.filter(score -> score.getValue() > THRESHOLD);
alerts.addSink(new AlertSink());
Flink SQL — Streams as tables:
-- Flink SQL: Real-time revenue aggregation
CREATE TABLE orders (
order_id STRING,
amount DECIMAL(10,2),
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH (
'connector' = 'kafka',
'topic' = 'orders',
'format' = 'json'
);
SELECT
TUMBLE_START(order_time, INTERVAL '1' MINUTE) AS window_start,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue
FROM orders
GROUP BY TUMBLE(order_time, INTERVAL '1' MINUTE);
2.3 Apache Spark Structured Streaming: Batch+Stream Hybrid
Spark processes streams using the micro-batching approach. It supports streaming while maintaining the strengths of batch processing.
Key strengths:
- Unified API: Same DataFrame API for both batch and streaming
- Vast ecosystem: Seamless integration with MLlib, GraphX, SparkSQL
- Mature community: Largest user base and documentation
- Photon engine: Databricks' C++ native engine with up to 12x performance boost
# Spark Structured Streaming - Real-time order analytics
from pyspark.sql import SparkSession
from pyspark.sql.functions import window, sum, count
spark = SparkSession.builder.appName("OrderAnalytics").getOrCreate()
orders = spark.readStream \
.format("kafka") \
.option("subscribe", "orders") \
.load()
order_stats = orders \
.withWatermark("order_time", "5 minutes") \
.groupBy(window("order_time", "1 minute")) \
.agg(
count("*").alias("order_count"),
sum("amount").alias("total_revenue")
)
order_stats.writeStream \
.format("iceberg") \
.outputMode("append") \
.option("checkpointLocation", "/checkpoint/orders") \
.toTable("catalog.db.order_stats")
2.4 Apache Beam: Unified Abstraction Layer
Beam is an abstraction layer that separates pipeline definition from execution engine. Write once and run on various runners like Flink, Spark, or Dataflow.
# Apache Beam - Multi-runner pipeline
import apache_beam as beam
with beam.Pipeline(options=pipeline_options) as p:
(p
| 'ReadKafka' >> beam.io.ReadFromKafka(
consumer_config=kafka_config,
topics=['orders'])
| 'ParseJSON' >> beam.Map(parse_order)
| 'WindowInto' >> beam.WindowInto(
beam.window.FixedWindows(60)) # 1-minute window
| 'CountPerWindow' >> beam.combiners.Count.Globally()
| 'WriteToGCS' >> beam.io.WriteToText('gs://bucket/output'))
2.5 Flink vs Spark vs Beam Comprehensive Comparison
| Feature | Apache Flink | Spark Structured Streaming | Apache Beam |
|---|---|---|---|
| Processing Model | True event-by-event | Micro-batching | Unified API (runner-dependent) |
| Latency | Milliseconds to sub-second | Seconds (100ms+) | Runner-dependent |
| State Management | Built-in checkpoints, RocksDB | Limited (watermark) | Runner-dependent |
| Accuracy | Exactly-once by default | Exactly-once capable | Runner-dependent |
| SQL Support | Flink SQL (powerful) | Spark SQL (best) | Beam SQL (limited) |
| Batch Processing | Good (bounded stream) | Best (native) | Good |
| ML Integration | Limited | MLlib / Spark ML | TFX integration |
| Learning Curve | Steep | Moderate | Steep |
| Best Use Cases | Real-time CEP, fraud detection | Batch+stream hybrid | Multi-engine portability |
| Major Users | Alibaba, Uber, Netflix | All Databricks customers | Google Cloud customers |
Selection guide:
- Millisecond latency is mandatory → Flink
- Both batch and streaming are important → Spark
- Multi-cloud portability → Beam
- GCP-centric → Beam + Dataflow
- Already using Databricks → Spark
2.6 Benchmarks: Real-World Performance Comparison
Nexocode 2024 Benchmark results (1 million events/second processing):
Processing Latency (p99):
┌────────────────────────────────────────────┐
│ Flink ████ 23ms │
│ Spark ████████████████ 450ms │
│ Beam/Flink████ 25ms │
│ Beam/Spark████████████████ 460ms │
└────────────────────────────────────────────┘
Throughput (events/second):
┌────────────────────────────────────────────┐
│ Flink ████████████████████ 5.2M │
│ Spark ████████████████ 3.8M │
│ Beam/Flink██████████████████ 4.8M │
└────────────────────────────────────────────┘
3. Orchestration: Airflow 3.0 vs Dagster vs Prefect vs Mage
3.1 What Is Orchestration?
Data pipeline orchestration manages the order, dependencies, scheduling, retries, and monitoring of tasks. In the modern data stack, the orchestrator serves as the "glue" connecting all components.
3.2 Apache Airflow 3.0: Evolution of the Throne
Airflow has been the de facto standard for data orchestration for over a decade. Airflow 3.0 in 2025 brought major innovations.
Airflow 3.0 Key Changes:
Airflow 3.0 Core Upgrades:
1. React-based Modern UI
- Real-time log streaming
- Improved DAG visualization
- Dark mode support
2. Event-Driven Scheduling
- Dataset-aware scheduling (auto-trigger on data arrival)
- Enhanced External Triggers
3. TaskFlow API 2.0
- Decorator-based Python-native DAG authoring
- Improved dynamic task mapping
4. Edge Labels & DAG Versioning
- DAG change history tracking
- A/B testing DAG support
5. Enhanced Security
- Improved RBAC
- Expanded Secret Backend integrations
# Airflow 3.0 - TaskFlow API Example
from airflow.decorators import dag, task
from datetime import datetime
@dag(schedule="@daily", start_date=datetime(2025, 1, 1), catchup=False)
def data_pipeline():
@task()
def extract():
"""Extract raw data from S3"""
import boto3
# Data extraction logic
return {"records": 15000, "source": "s3://datalake/raw/"}
@task()
def transform(data: dict):
"""Transform data via dbt"""
import subprocess
subprocess.run(["dbt", "run", "--select", "staging+"])
return {"transformed": data["records"], "models": 12}
@task()
def load(data: dict):
"""Load results into ClickHouse"""
# ClickHouse loading logic
return {"loaded": data["transformed"]}
@task()
def notify(result: dict):
"""Send Slack notification"""
# Slack notification logic
pass
raw = extract()
transformed = transform(raw)
loaded = load(transformed)
notify(loaded)
data_pipeline()
3.3 Dagster: The Asset-Centric Paradigm
Dagster is designed around the concept of "Software-Defined Assets." It defines pipelines centered on assets rather than tasks.
Key differentiators:
- Asset Graph: Explicitly manages dependencies between data assets
- Native dbt integration: Automatically converts dbt projects into Dagster assets
- Type system: Strong type validation via I/O managers
- Observability: Automatic tracking of asset materialization history
# Dagster - Software-Defined Assets Example
from dagster import asset, AssetExecutionContext
from dagster_dbt import DbtCliResource, dbt_assets
@asset(
description="Extract raw order data from S3",
group_name="raw",
compute_kind="python"
)
def raw_orders(context: AssetExecutionContext):
"""Extract order data from S3"""
import pandas as pd
df = pd.read_parquet("s3://datalake/raw/orders/")
context.log.info(f"Extracted {len(df)} orders")
return df
@asset(
description="Validate order data quality",
group_name="validated",
compute_kind="great_expectations"
)
def validated_orders(raw_orders):
"""Validate data quality with Great Expectations"""
# Quality validation logic
assert raw_orders["amount"].min() >= 0, "Negative amounts found"
return raw_orders
# Automatic dbt asset integration
@dbt_assets(manifest=dbt_manifest_path)
def dbt_models(context: AssetExecutionContext, dbt: DbtCliResource):
yield from dbt.cli(["build"], context=context).stream()
3.4 Prefect: The Simplest Orchestrator
Prefect aims for minimal boilerplate and best-in-class failure handling.
# Prefect - Concise Pipeline
from prefect import flow, task
from prefect.tasks import task_input_hash
from datetime import timedelta
@task(retries=3, retry_delay_seconds=60,
cache_key_fn=task_input_hash,
cache_expiration=timedelta(hours=1))
def extract_data(source: str) -> dict:
"""Extraction with automatic retries and caching"""
# Data extraction
return {"data": [...], "count": 15000}
@task(log_prints=True)
def transform_data(raw: dict) -> dict:
"""Transformation logic"""
print(f"Processing {raw['count']} records")
return {"transformed": raw["count"]}
@flow(name="daily-etl", log_prints=True)
def daily_pipeline():
raw = extract_data("s3://datalake/raw/")
result = transform_data(raw)
print(f"Pipeline complete: {result}")
# Execute
daily_pipeline()
3.5 Mage AI: All-in-One Platform
Mage aims to be an integrated development environment (IDE) for data engineering. You can visually build pipelines in a notebook-style UI.
3.6 Orchestrator Comprehensive Comparison
| Feature | Airflow 3.0 | Dagster | Prefect | Mage |
|---|---|---|---|---|
| Paradigm | DAG/task-centric | Asset-centric | Flow/task | Notebook+pipeline |
| Learning Curve | Medium-high | Medium | Low | Low |
| dbt Integration | Via Provider | Native (best) | Basic | Basic |
| UI | React (major improvement) | Dagit (excellent) | Cloud UI | Notebook style |
| Scaling | KubernetesExecutor | K8s / ECS | Kubernetes | Kubernetes |
| Community | Largest (10+ years) | Growing fast | Medium | Growing |
| Provider Ecosystem | 1000+ | 100+ | 200+ | 50+ |
| Failure Handling | Medium | Good | Best | Good |
| Pricing (Cloud) | MWAA cost | Dagster+ | Prefect Cloud | Mage Pro |
| Recommended For | Large/complex pipelines | dbt-centric projects | Quick start, simplicity | Exploratory work |
Final recommendations:
- Enterprise, complex pipelines → Airflow 3.0
- dbt-centric, analytics engineering → Dagster
- Quick start, simplicity first → Prefect
- Data science + engineering → Mage
4. dbt: The SQL Renaissance
4.1 How dbt Changed the Data Transformation Paradigm
dbt (data build tool) handles the "T (Transform)" in ELT. It enables building data transformation pipelines using only SQL while applying software engineering best practices (version control, testing, documentation) to data transformations.
dbt's growth:
dbt Milestones:
- 2016: Fishtown Analytics founded
- 2020: dbt Cloud launched
- 2022: Rebranded to dbt Labs, Series D $222M
- 2023: MetricFlow open-sourced
- 2024: Semantic Layer GA in dbt Cloud
- 2025: Crossed $100M+ ARR, established de facto standard status
4.2 dbt Core vs dbt Cloud
| Feature | dbt Core (OSS) | dbt Cloud |
|---|---|---|
| Price | Free | Team from $100/mo, Enterprise custom |
| Execution | CLI (local/CI) | Managed execution environment |
| Scheduling | External (Airflow, etc.) | Built-in scheduler |
| IDE | VS Code + extensions | Cloud IDE (browser) |
| Semantic Layer | MetricFlow CLI | Managed API |
| Docs site | Self-hosted | Auto-generated/hosted |
| CI/CD | Self-configured | Built-in Slim CI |
4.3 Production dbt Project Structure
dbt_project/
├── dbt_project.yml
├── packages.yml
├── profiles.yml
├── models/
│ ├── staging/ # Source data cleansing
│ │ ├── stg_orders.sql
│ │ ├── stg_customers.sql
│ │ └── _staging.yml # Tests & docs
│ ├── intermediate/ # Business logic composition
│ │ ├── int_order_items.sql
│ │ └── _intermediate.yml
│ ├── marts/ # Final analytics tables
│ │ ├── finance/
│ │ │ ├── fct_revenue.sql
│ │ │ └── dim_customers.sql
│ │ └── marketing/
│ │ └── fct_campaigns.sql
│ └── metrics/ # MetricFlow metrics
│ └── revenue.yml
├── tests/ # Custom tests
│ └── assert_positive_revenue.sql
├── macros/ # Reusable SQL macros
│ └── cents_to_dollars.sql
├── seeds/ # Static data (CSV)
│ └── country_codes.csv
└── snapshots/ # SCD Type 2 snapshots
└── snap_customers.sql
Core model examples:
-- models/staging/stg_orders.sql
WITH source AS (
SELECT * FROM {{ source('raw', 'orders') }}
),
renamed AS (
SELECT
id AS order_id,
user_id AS customer_id,
status AS order_status,
amount_cents / 100.0 AS amount_dollars,
created_at AS ordered_at
FROM source
WHERE status != 'cancelled'
)
SELECT * FROM renamed
-- models/marts/finance/fct_revenue.sql
WITH orders AS (
SELECT * FROM {{ ref('stg_orders') }}
),
customers AS (
SELECT * FROM {{ ref('dim_customers') }}
),
final AS (
SELECT
o.order_id,
o.customer_id,
c.customer_segment,
o.amount_dollars,
o.ordered_at,
DATE_TRUNC('month', o.ordered_at) AS order_month
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_status = 'completed'
)
SELECT * FROM final
# models/staging/_staging.yml
version: 2
models:
- name: stg_orders
description: 'Cleansed order data'
columns:
- name: order_id
description: 'Unique order identifier'
tests:
- unique
- not_null
- name: amount_dollars
description: 'Order amount in dollars'
tests:
- not_null
- dbt_utils.accepted_range:
min_value: 0
max_value: 100000
4.4 MetricFlow and the Semantic Layer
MetricFlow is a metric definition framework open-sourced by dbt Labs. Define business metrics in one place, and get consistent results across various BI tools.
# models/metrics/revenue.yml
semantic_models:
- name: orders
defaults:
agg_time_dimension: ordered_at
model: ref('fct_revenue')
entities:
- name: order_id
type: primary
- name: customer_id
type: foreign
measures:
- name: revenue
agg: sum
expr: amount_dollars
- name: order_count
agg: count
expr: order_id
dimensions:
- name: ordered_at
type: time
- name: customer_segment
type: categorical
metrics:
- name: monthly_revenue
type: simple
type_params:
measure: revenue
- name: revenue_per_customer
type: derived
type_params:
expr: monthly_revenue / active_customers
metrics:
- name: monthly_revenue
- name: active_customers
4.5 dbt Best Practices
Top 10 dbt Project Best Practices:
1. Enforce layer structure: staging -> intermediate -> marts
2. Complete all renaming and type casting in staging
3. Apply unique + not_null tests on all models
4. Monitor data freshness with source freshness checks
5. Optimize large tables with incremental models
6. Leverage dbt-utils, dbt-expectations via packages.yml
7. SQL linting with pre-commit hooks (sqlfluff)
8. Use slim CI (state:modified+) in CI pipelines
9. Separate profiles by environment (dev / staging / prod)
10. Centralize metric management with Semantic Layer
5. Lakehouse: Iceberg vs Delta Lake vs Hudi
5.1 Why Lakehouse Is Needed
Traditionally, data lakes and data warehouses were separate systems. Data lakes stored all formats cheaply but lacked ACID transactions, while warehouses provided fast analytical queries but were expensive.
The lakehouse combines both:
Lakehouse = Low-cost storage of data lakes + ACID/performance of warehouses
Traditional Architecture:
[Sources] -> [Data Lake (S3)] -> [ETL] -> [Data Warehouse (Redshift)]
High cost, duplicate storage
Lakehouse Architecture:
[Sources] -> [Object Storage (S3)] + [Table Format (Iceberg)]
Low cost ACID, schema evolution, time travel
└──> [Analytics Engine (Spark/Trino/Flink)] Direct query
5.2 Apache Iceberg: The Engine-Independent King
Iceberg is an open table format developed at Netflix. It's not tied to any specific engine, allowing Spark, Flink, Trino, Presto, Hive, and more to access the same tables.
Key features:
- Schema Evolution: Add/drop/rename columns without data rewriting
- Partition Evolution: Change partition schema while preserving existing data
- Time Travel: Query snapshots at specific points in time
- Hidden Partitioning: Automatic optimization without users worrying about partitions
- Multi-engine: Same table format regardless of engine
-- Iceberg table creation and usage
CREATE TABLE catalog.db.orders (
order_id BIGINT,
customer_id BIGINT,
amount DECIMAL(10,2),
status STRING,
ordered_at TIMESTAMP
)
USING iceberg
PARTITIONED BY (days(ordered_at));
-- Time travel: query data as of yesterday
SELECT * FROM catalog.db.orders
VERSION AS OF '2025-03-21T00:00:00';
-- Snapshot-based incremental reads
SELECT * FROM catalog.db.orders
WHERE ordered_at > (
SELECT max(ordered_at)
FROM catalog.db.orders
VERSION AS OF 'snapshot_id_123'
);
Tabular Acquisition (2024): Databricks acquired Tabular, the commercial company behind Iceberg. This caused major shifts in Iceberg's commercial ecosystem, with Snowflake and other vendors strengthening their independent Iceberg support.
5.3 Delta Lake: The Heart of the Databricks Ecosystem
Delta Lake is an open-source table format developed by Databricks. Its deep integration with Spark is its key strength.
Key features:
- UniForm: Automatic Iceberg and Hudi-compatible metadata generation
- Liquid Clustering: Automatic data layout optimization replacing Z-Order
- Change Data Feed: Capture CDC events directly from Delta tables
- Deletion Vectors: Efficient deletes/updates without file rewriting
# Delta Lake - UniForm for Iceberg compatibility
from delta.tables import DeltaTable
# Create Delta table (UniForm enabled)
spark.sql("""
CREATE TABLE catalog.db.events (
event_id BIGINT,
event_type STRING,
payload STRING,
event_time TIMESTAMP
)
USING DELTA
TBLPROPERTIES (
'delta.universalFormat.enabledFormats' = 'iceberg'
)
""")
# Delta table MERGE (Upsert)
delta_table = DeltaTable.forName(spark, "catalog.db.events")
delta_table.alias("target").merge(
new_events.alias("source"),
"target.event_id = source.event_id"
).whenMatchedUpdateAll() \
.whenNotMatchedInsertAll() \
.execute()
5.4 Apache Hudi: The Real-Time CDC Champion
Hudi (Hadoop Upserts Deletes and Incrementals) was developed at Uber, optimized for record-level indexing and real-time CDC processing.
Key features:
- Record-level index: Extremely fast individual record upserts
- Copy-on-Write vs Merge-on-Read: Optimal strategy selection per workload
- Incremental Query: Efficiently read only changed records
- Concurrency Control: Optimistic concurrency control
5.5 Iceberg vs Delta Lake vs Hudi Comprehensive Comparison
| Feature | Apache Iceberg | Delta Lake | Apache Hudi |
|---|---|---|---|
| Developer | Netflix (now Apache) | Databricks | Uber (now Apache) |
| Best For | Multi-engine, petabyte | Databricks ecosystem | Real-time CDC/Upsert |
| Engine Support | Spark, Flink, Trino, Presto | Spark (optimal), others limited | Spark, Flink |
| Schema Evolution | Best (full evolution) | Good | Good |
| Partition Evolution | Best (hidden partitioning) | Liquid Clustering | Limited |
| CDC Support | Incremental reads only | Change Data Feed | Native (best) |
| Time Travel | Snapshot-based | Version-based | Commit timeline |
| Concurrency | Optimistic (branch support) | Optimistic | Optimistic |
| Compatibility | Standard (open) | UniForm (Iceberg compat) | Limited |
| Catalog | Polaris, REST, Hive | Unity Catalog | Internal timeline |
| Community Size | Growing fast (largest) | Databricks-led | Medium |
Selection guide:
- Multi-engine, vendor-independent → Iceberg
- Already using Databricks → Delta Lake (with UniForm for Iceberg compat)
- Real-time CDC, frequent upserts → Hudi
- New project, maximum flexibility → Iceberg
6. Real-Time Analytics: ClickHouse vs Druid vs Pinot vs StarRocks
6.1 Why You Need an OLAP Engine
Once you've stored data in your lakehouse, you need an OLAP engine that can execute analytical queries in milliseconds. PostgreSQL or MySQL simply cannot handle real-time aggregation queries across billions of rows.
6.2 ClickHouse: The Fastest Open-Source OLAP
ClickHouse is a columnar OLAP database developed by Yandex. It can scan billions of rows per second even on a single node.
-- ClickHouse - Real-time dashboard queries
CREATE TABLE events (
event_id UInt64,
user_id UInt32,
event_type LowCardinality(String),
properties Map(String, String),
event_time DateTime64(3)
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_time)
ORDER BY (event_type, user_id, event_time);
-- Materialized View for real-time aggregation
CREATE MATERIALIZED VIEW hourly_events_mv
ENGINE = SummingMergeTree()
ORDER BY (event_type, hour)
AS SELECT
event_type,
toStartOfHour(event_time) AS hour,
count() AS event_count,
uniqHLL12(user_id) AS unique_users
FROM events
GROUP BY event_type, hour;
-- Millisecond responses across billions of rows
SELECT
event_type,
sum(event_count) AS total_events,
sum(unique_users) AS total_users
FROM hourly_events_mv
WHERE hour >= now() - INTERVAL 24 HOUR
GROUP BY event_type
ORDER BY total_events DESC
LIMIT 20;
6.3 Apache Druid: Real-Time Ingestion + Analytics
Druid is an OLAP database designed to handle real-time data ingestion and analytics simultaneously. It can ingest data directly from Kafka while concurrently processing queries.
6.4 Apache Pinot: LinkedIn's Real-Time Analytics
Pinot is an OLAP database developed at LinkedIn, optimized for user-facing analytics. It powers LinkedIn's "Who viewed your profile," Uber's UberEats restaurant dashboards, and more.
6.5 StarRocks: The Next-Generation OLAP Challenger
StarRocks is a next-generation OLAP engine supporting the MySQL-compatible protocol. Its unique lakehouse analytics capability allows direct querying of Iceberg, Hudi, and Delta Lake tables without separate data loading.
6.6 OLAP Engine Comprehensive Comparison
| Feature | ClickHouse | Apache Druid | Apache Pinot | StarRocks |
|---|---|---|---|---|
| Developer | Yandex → ClickHouse Inc | Imply | LinkedIn → StarTree | CelerData |
| Architecture | Single binary | MSQ (complex) | Helix-based | MPP (simple) |
| Real-time Ingestion | Kafka engine | Native (best) | Native (good) | Routine Load |
| Query Performance | Best (single node) | Good | Good | Best (MPP) |
| SQL Compatibility | ANSI SQL | Druid SQL | PQL + SQL | MySQL compatible |
| Join Performance | Limited | Limited | Limited | Good (MPP) |
| Lakehouse Queries | Limited | Limited | Limited | Native (best) |
| Operational Complexity | Low | High | Medium | Low |
| Learning Curve | Low | High | Medium | Low (MySQL) |
| Scaling | Horizontal | Horizontal | Horizontal | Horizontal |
Benchmarks (ClickBench 2024):
TPC-H 10GB Query Performance (seconds, lower is better):
┌──────────────────────────────────────────────────┐
│ StarRocks 3.x ████ 2.1s (2.2x faster than CH) │
│ ClickHouse █████████ 4.6s │
│ Druid ████████████████████ 18.7s │
│ Pinot ██████████████████ 16.3s │
└──────────────────────────────────────────────────┘
Selection guide:
- Best single-node performance, simple operations → ClickHouse
- Kafka-native real-time ingestion → Druid
- User-facing analytics at scale → Pinot
- Direct lakehouse queries, MySQL compatible → StarRocks
7. Data Mesh and Data Contracts
7.1 Data Mesh: Decentralized Data Architecture
Data Mesh is a decentralized data architecture paradigm proposed by Zhamak Dehghani. Instead of a central data team managing all data, domain teams own and manage their own Data Products.
The 4 Principles of Data Mesh:
1. Domain Ownership
-> Each domain team owns their data
2. Data as a Product
-> Apply SLA, documentation, quality guarantees to data
3. Self-Serve Platform
-> Infrastructure team provides data platform tools
4. Federated Governance
-> Balance between enterprise standards + domain autonomy
Gartner 2025 Forecast: 70% of large enterprises are piloting or planning Data Mesh. However, fully successful Data Mesh transformations remain few.
7.2 Data Contracts: The Promise of Data APIs
A Data Contract is a formal agreement between data producers and consumers. Like API specs, it specifies data schema, quality, and SLA.
# data-contract.yml example
dataContractSpecification: 0.9.3
id: orders-contract
info:
title: 'Orders Data Contract'
version: 2.1.0
owner: order-domain-team
contact:
name: 'Orders Team'
email: orders@company.com
servers:
production:
type: iceberg
catalog: polaris
database: orders_db
table: orders
models:
orders:
description: 'Completed order data'
fields:
order_id:
type: bigint
required: true
unique: true
description: 'Unique order identifier'
customer_id:
type: bigint
required: true
amount:
type: decimal
required: true
minimum: 0
description: 'Order amount (USD)'
ordered_at:
type: timestamp
required: true
quality:
type: SodaCL
specification:
checks for orders:
- row_count > 0
- missing_count(order_id) = 0
- duplicate_count(order_id) = 0
- avg(amount) between 10 and 500
sla:
freshness: 1 hour
availability: 99.9%
latency: p99 < 500ms
7.3 Data Mesh + Data Fabric Hybrid
The realistic trend in 2025 is a hybrid approach combining Data Mesh and Data Fabric.
Data Mesh + Fabric Hybrid:
┌──────────────────────────┐
│ Data Fabric Layer │
│ (Unified Governance/Catalog)│
└──────────┬───────────────┘
┌──────────────────┼──────────────────┐
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Order Domain│ │Customer Domn│ │Product Domn │
│ Data Product│ │ Data Product│ │ Data Product│
│ (Iceberg) │ │ (Iceberg) │ │ (Iceberg) │
└─────────────┘ └─────────────┘ └─────────────┘
Tooling ecosystem:
- Catalog: OpenMetadata, DataHub, Amundsen
- Quality: Great Expectations, Soda, Monte Carlo
- Contracts: Datakin, Schemata, Bitol
- Lineage: Marquez, OpenLineage
8. Production Architecture: Real-Time Recommendation System
8.1 Requirements
Assume we're building a real-time personalized recommendation system for a large e-commerce platform.
Requirements:
- Daily active users: 5 million
- Events per second: 50,000
- Recommendation latency: p99 < 100ms
- Data sources: Click streams, purchases, searches, inventory
- Feature store: Combined real-time + batch features
- A/B testing: Performance comparison across model versions
8.2 Architecture Design
Real-Time Recommendation System Architecture:
[Click/Purchase/Search Events]
|
v
┌─────────┐ ┌──────────────┐
│ Kafka │────>│ Flink (CEP) │──> Real-time feature computation
│ Cluster │ │ Window Agg. │ (recent 5-min click categories)
└────┬────┘ └──────┬───────┘
│ │
v v
┌─────────┐ ┌──────────────┐
│ Iceberg │ │ Redis │──> Real-time feature serving
│ (raw) │ │ Feature Store│ (p99 < 5ms)
└────┬────┘ └──────────────┘
│ ^
v │
┌─────────┐ ┌──────────────┐
│ dbt │────>│ ClickHouse │──> Batch feature computation
│(transform│ │ (agg/query) │ (30-day purchase patterns)
└────┬────┘ └──────────────┘
│
v
┌─────────────────┐ ┌──────────────┐
│ ML Model Serving │<───│ Feature Store │
│ (TorchServe/ │ │ (Redis+DuckDB)│
│ Triton) │ └──────────────┘
└────────┬────────┘
│
v
┌─────────────────┐
│ API Gateway │──> Serve recommendations to users
│ (p99 < 100ms) │
└─────────────────┘
8.3 Core Implementation Code
Flink real-time feature computation:
// Real-time user behavior feature computation
DataStream<UserFeature> realtimeFeatures = clickEvents
.keyBy(ClickEvent::getUserId)
.window(SlidingEventTimeWindows.of(
Time.minutes(5), Time.minutes(1)))
.aggregate(new UserBehaviorAggregator());
// Store to Redis feature store
realtimeFeatures.addSink(
new RedisSink<>(redisConfig, new FeatureRedisMapper()));
dbt batch feature model:
-- models/features/user_purchase_patterns.sql
-- 30-day purchase pattern features (batch)
WITH purchase_stats AS (
SELECT
customer_id,
COUNT(*) AS purchase_count_30d,
SUM(amount_dollars) AS total_spend_30d,
AVG(amount_dollars) AS avg_order_value_30d,
COUNT(DISTINCT category) AS unique_categories_30d,
MAX(ordered_at) AS last_purchase_at
FROM {{ ref('fct_revenue') }}
WHERE ordered_at >= CURRENT_DATE - INTERVAL '30' DAY
GROUP BY customer_id
)
SELECT
customer_id,
purchase_count_30d,
total_spend_30d,
avg_order_value_30d,
unique_categories_30d,
DATEDIFF('day', last_purchase_at, CURRENT_DATE) AS days_since_last_purchase
FROM purchase_stats
Airflow DAG:
# Recommendation system daily pipeline
@dag(schedule="0 2 * * *", catchup=False)
def recommendation_pipeline():
@task()
def run_dbt_features():
"""Generate batch features with dbt"""
import subprocess
result = subprocess.run(
["dbt", "run", "--select", "features+"],
capture_output=True, text=True
)
return result.returncode == 0
@task()
def export_to_feature_store(dbt_success: bool):
"""Export batch features to Feature Store"""
if not dbt_success:
raise Exception("dbt run failed")
# ClickHouse -> Redis feature store sync
pass
@task()
def retrain_model():
"""Retrain ML model"""
# Daily model retraining logic
pass
@task()
def validate_model(retrain_result):
"""Validate model performance"""
# A/B test metric comparison
pass
dbt_result = run_dbt_features()
export = export_to_feature_store(dbt_result)
retrain = retrain_model()
validate_model(retrain)
recommendation_pipeline()
8.4 Cost Optimization
Monthly Estimated Costs (AWS, 5M daily users):
| Component | Instance Type | Monthly Cost |
|-----------------|------------------------|---------------|
| Kafka (MSK) | kafka.m5.2xlarge x3 | ~$2,100 |
| Flink (EMR) | m5.2xlarge x4 | ~$2,800 |
| Iceberg (S3) | 50TB storage | ~$1,150 |
| ClickHouse | m5.4xlarge x3 | ~$4,200 |
| Redis | r6g.2xlarge x2 | ~$1,800 |
| Airflow (MWAA) | mw1.medium | ~$350 |
Total estimated cost: ~$12,400/month
Cost optimization strategies:
1. Spot instances for Flink workers -> 60% savings
2. Iceberg compaction optimization -> 30% storage savings
3. ClickHouse TTL policies -> Auto-delete old data
4. Reserved Instances -> 40% savings for long-term workloads
9. Certifications and Learning Roadmap
9.1 Recommended 2025 Certifications
| Certification | Provider | Difficulty | Core Content | Recommended For |
|---|---|---|---|---|
| DP-700 | Microsoft | Medium | Fabric Analytics Engineer | Azure ecosystem |
| Data Engineer Associate | Databricks | Medium | Spark, Delta Lake, Unity | Databricks users |
| Professional DE | Google Cloud | High | BigQuery, Dataflow, GCS | GCP ecosystem |
| Data Engineer | AWS (DEA-C01) | Medium | Glue, Redshift, EMR | AWS ecosystem |
| dbt Analytics Engineering | dbt Labs | Low-Med | dbt Core, modeling | Analytics engineers |
| CCA Spark and Hadoop | Cloudera | Medium | Spark, HDFS, Hive | On-premises |
9.2 12-Month Learning Roadmap
12-Month Data Engineer Learning Path:
[Months 1-3: Foundations]
├── Advanced SQL (window functions, CTEs, optimization)
├── Python data processing (pandas, polars)
├── Linux / Docker basics
└── Git / CI/CD basics
[Months 4-6: Core Tools]
├── Apache Spark (DataFrame API, SparkSQL)
├── dbt (project structure, tests, documentation)
├── Airflow (DAG authoring, operators)
└── Kafka basics (producers, consumers, topics)
[Months 7-9: Advanced]
├── Apache Flink (stream processing, state management)
├── Iceberg / Delta Lake (lakehouse)
├── ClickHouse (OLAP analytics)
└── Great Expectations (data quality)
[Months 10-12: Production & Certification]
├── Complete 3 portfolio projects
├── Earn 1-2 certifications
├── Learn Data Mesh / governance
└── Start a technical blog
9.3 Recommended Learning Resources
Free resources:
- DataTalksClub DE Zoomcamp: Free online bootcamp (16 weeks)
- Databricks Academy: Free Spark, Delta Lake courses
- dbt Learn: Official dbt tutorials
- ClickHouse University: Official free courses
Paid resources:
- Zach Wilson's Data Engineering Bootcamp: Project-focused
- O'Reilly "Fundamentals of Data Engineering": The bible
- Udemy "Apache Flink Master Class": Advanced Flink
10. Three Portfolio Projects
Project 1: Real-Time Taxi Fare Prediction Pipeline
Tech Stack: Kafka + Flink + Iceberg + dbt + ClickHouse
Architecture:
NYC Taxi Data (API) -> Kafka -> Flink (real-time features) -> Iceberg
|
dbt (batch transform)
|
ClickHouse (dashboard)
Implementation highlights:
1. Real-time regional demand forecasting features with Flink
2. Iceberg partition evolution (daily -> hourly)
3. dbt incremental models for daily aggregation
4. ClickHouse Materialized Views for real-time dashboards
5. Airflow DAG orchestrating the entire pipeline
Project 2: E-Commerce CDC Pipeline
Tech Stack: Debezium + Kafka + Spark + Delta Lake + dbt
Architecture:
PostgreSQL (OLTP) -> Debezium (CDC) -> Kafka -> Spark Streaming -> Delta Lake
|
dbt (layered transform)
|
Superset (BI dashboard)
Implementation highlights:
1. Real-time PostgreSQL change capture with Debezium
2. CDC event processing with Spark Structured Streaming
3. SCD Type 2 implementation with Delta Lake MERGE
4. dbt staging/intermediate/marts structure
5. Superset real-time revenue/inventory dashboard
Project 3: Data Contract-Based Data Mesh
Tech Stack: OpenMetadata + Soda + dbt + Iceberg + Dagster
Architecture:
Domain A (Orders) ──> Data Contract (YAML) ──> Iceberg
Domain B (Customers) -> Data Contract (YAML) -> Iceberg
Domain C (Products) -> Data Contract (YAML) -> Iceberg
|
OpenMetadata (catalog/lineage)
Soda (quality validation)
Dagster (orchestration)
Implementation highlights:
1. Define Data Contract YAMLs for 3 domains
2. Automated data quality validation with Soda
3. Lineage and catalog management with OpenMetadata
4. Pipeline building with Dagster Software-Defined Assets
5. Enterprise metric layer (MetricFlow)
Quizzes
Quiz 1: Stream Processing Engine Selection
Q: You need to build a credit card fraud detection system. Decisions must be made within 50ms of a transaction, and you need to analyze trading patterns from the last 10 minutes. Which stream processing engine is most suitable?
A: Apache Flink
Why Flink is optimal:
- Event-by-event processing: Flink performs true per-event processing, so each transaction can be handled immediately.
- Sub-millisecond latency: Unlike Spark's micro-batching (minimum 100ms+), Flink guarantees millisecond-level latency.
- Checkpoint-based state management: Reliably maintains the 10-minute sliding window state in RocksDB while guaranteeing exactly-once semantics.
- CEP (Complex Event Processing): The Flink CEP library allows declarative definition of complex fraud patterns.
Spark's micro-batching nature makes it difficult to meet the 50ms constraint, and Beam's performance varies by runner, making Flink the best choice for this scenario.
Quiz 2: Lakehouse Format Selection
Q: Your company uses Spark, Trino, and Flink, processing hundreds of GBs of CDC data daily. Schemas change frequently. Which table format should you choose?
A: Apache Iceberg
Why Iceberg is optimal:
- Multi-engine support: Spark, Trino, and Flink all support Iceberg as first-class citizens. Delta Lake has limited non-Spark engine support, and Hudi has weak Trino support.
- Schema evolution: Iceberg supports full schema evolution. Adding, dropping, renaming, and type-changing columns requires no data rewriting.
- Partition evolution: Hidden partitioning ensures partition schema changes have no impact on existing data or queries.
If extremely frequent record-level upserts are the core requirement, Hudi could be considered, but if multi-engine support and schema evolution are priorities, Iceberg is the best choice.
Quiz 3: dbt Project Structure
Q: What is the difference between staging and marts models in a dbt project, and why is this layered structure necessary?
A:
Staging models:
- Cleanse source data in a 1:1 mapping
- Only basic transformations: column renaming, type casting, filtering
- Contain no business logic
- One staging model per source
Marts models:
- Final analytics tables that answer business questions
- Join multiple staging/intermediate models and apply business logic
- Divided into fact and dimension tables
- Grouped by business domain (finance, marketing, etc.)
Why the layered structure is necessary:
- DRY principle: When sources change, only staging needs modification, propagating to all downstream models
- Testability: Independent testing at each layer
- Debugging: Quickly identify which layer a problem originated from
- Team collaboration: Analysts manage marts only; engineers manage staging
Quiz 4: Orchestrator Selection
Q: A 5-person analytics engineering team wants to run dbt-centric data pipelines. They have no prior Airflow experience and need to set up a production environment as quickly as possible. Which orchestrator would you recommend?
A: Dagster
Why Dagster is optimal:
- Native dbt integration: Dagster automatically converts dbt projects into Software-Defined Assets. Dependencies between dbt models are automatically reflected in the Dagster asset graph.
- Low learning curve (for dbt users): Dagster's asset-centric paradigm feels natural to dbt users. It's more intuitive than Airflow's DAG/Operator concepts.
- Quick production setup: Dagster Cloud lets you start immediately without infrastructure management.
- Observability: Automatically tracks asset materialization history, data lineage, and metadata.
Airflow's initial setup and operations are complex for inexperienced teams, and Prefect's dbt integration isn't as deep as Dagster's.
Quiz 5: OLAP Engine Selection
Q: You've already built an Iceberg lakehouse and want to run analytical queries directly on Iceberg tables without separate data loading. Your team is familiar with MySQL. Which OLAP engine is most suitable?
A: StarRocks
Why StarRocks is optimal:
- Native lakehouse queries: StarRocks can query Iceberg, Delta Lake, and Hudi tables directly. No separate data ingestion needed, reducing data duplication and sync costs.
- MySQL-compatible protocol: MySQL clients and drivers work as-is, so MySQL-familiar teams can adapt immediately.
- MPP architecture: Superior performance for large join queries compared to ClickHouse.
- Simple operations: Simpler architecture than Druid, much easier to operate.
ClickHouse has limited direct Iceberg query support, and Druid/Pinot have high operational complexity and are not MySQL-compatible.
References
Official Documentation
- Apache Flink Documentation — Official Flink docs
- Apache Spark Documentation — Official Spark docs
- dbt Documentation — Official dbt docs
- Apache Iceberg Documentation — Official Iceberg docs
- ClickHouse Documentation — Official ClickHouse docs
- Apache Airflow Documentation — Official Airflow docs
- Dagster Documentation — Official Dagster docs
Architecture and Comparisons
- Nexocode: Flink vs Spark vs Beam Benchmark 2024 — Stream processing engine benchmarks
- Databricks: Delta Lake UniForm — Official Delta UniForm docs
- ClickBench: OLAP Database Benchmarks — OLAP performance comparisons
- Data Mesh Architecture by Zhamak Dehghani — Data Mesh principles and architecture
Learning Resources
- DataTalksClub Data Engineering Zoomcamp — Free bootcamp
- O'Reilly: Fundamentals of Data Engineering — Data engineering bible
- dbt Learn — Official dbt learning courses
- Databricks Academy — Free Spark/Delta courses
- Apache Iceberg: The Definitive Guide (O'Reilly) — Advanced Iceberg learning
Trends and Analysis
- Gartner: Modern Data Stack Trends 2025 — Data engineering trends
- a16z: The Modern Data Stack (2024 Update) — VC perspective on the data stack