Hamutaro - Hamtaro 4

Database/Engineering

[DE] 데이터 νŒŒμ΄ν”„λΌμΈ 섀계 원칙

carsumin 2026. 5. 22. 11:14
λ©±λ“±μ„± (Idempotency)
  • 같은 νŒŒμ΄ν”„λΌμΈμ„ μ—¬λŸ¬ 번 싀행해도 κ²°κ³Όκ°€ 항상 κ°™μ•„μ•Ό ν•œλ‹€.
μ™œ ν•„μš”ν• κΉŒ? νŒŒμ΄ν”„λΌμΈμ€ μ–Έμ œλ“  μ‹€νŒ¨ν•  수 μžˆλ‹€.

μƒˆλ²½ 2μ‹œ νŒŒμ΄ν”„λΌμΈ μ‹€ν–‰
-> 절반쯀 μ‹€ν–‰λ˜λ‹€κ°€ μ„œλ²„ μž₯μ• λ‘œ μ‹€νŒ¨
-> μƒˆλ²½ 3μ‹œ μž¬μ‹€ν–‰
-> 데이터가 두 번 λ“€μ–΄κ°€λ©΄?
  • 멱등성이 μ—†μœΌλ©΄ μž¬μ‹€ν–‰ν•  λ•Œλ§ˆλ‹€ 데이터가 μ€‘λ³΅λœλ‹€.

1. λ‚˜μœ μ˜ˆμ‹œ - μ‹€ν–‰ν•  λ•Œλ§ˆλ‹€ 데이터가 μŒ“μž„

INSERT INTO fct_orders
SELECT * FROM stg_orders
WHERE date = '2024-01-01';

 

2. 쒋은 μ˜ˆμ‹œ - λ¨Όμ € μ§€μš°κ³  λ‹€μ‹œ λ„£μŒ

DELETE FROM fct_orders WHERE date = '2024-01-01';

INSERT INTO fct_orders
SELECT * FROM stg_orders
WEHRE date = '2024-01-01';

 

λ˜λŠ” MERGE / UPSERT μ‚¬μš©

MERGE INTO fct_orders AS target
USING stg_orders AS source
ON target.order_id = source.order_id
WHEN MATCHED THEN UPDATE SET ...
WHEN NOT MATCHED THEN INSERT ...

 

 

재처리 (Backfill)
  • κ³Όκ±° 데이터λ₯Ό μ†ŒκΈ‰ν•΄μ„œ λ‹€μ‹œ μ²˜λ¦¬ν•˜λŠ” 것이닀.
    • 멱등성이 μ—†μœΌλ©΄ backfill ν•  λ•Œ 데이터가 μ€‘λ³΅λ˜κ±°λ‚˜ 꼬인닀. λ©±λ“±μ„±κ³Ό μž¬μ²˜λ¦¬λŠ” 항상 ν•¨κ»˜ 섀계해야 ν•œλ‹€.
상황 : 2024λ…„ 1μ›” λ³€ν™˜ λ‘œμ§μ— 버그가 μžˆμ—ˆμŒ
-> 1μ›” 데이터λ₯Ό μ „λΆ€ λ‹€μ‹œ μ²˜λ¦¬ν•΄μ•Ό 함
-> 멱등성이 μžˆμ–΄μ•Ό μ•ˆμ „ν•˜κ²Œ 재처리 κ°€λŠ₯

 

  • Airflowμ—μ„œλŠ” μ΄λ ‡κ²Œ μ‹€ν–‰ν•œλ‹€.
airflow dags backfill \
    -- start-date 2024-01-01 \
    -- end-date 2024-01-31 \
    my_pipeline

 

 

νŒŒν‹°μ…”λ‹ (Partitioning)
  • λŒ€μš©λŸ‰ ν…Œμ΄λΈ”μ„ λ‚ μ§œ λ“± κΈ°μ€€μœΌλ‘œ λ‚˜λˆ μ„œ μ €μž₯ν•˜λŠ” 것이닀.
    • νŒŒν‹°μ…”λ‹μ€ 쿼리 μ„±λŠ₯뿐 μ•„λ‹ˆλΌ λΉ„μš©μ—λ„ 직접적인 영ν–₯을 μ€€λ‹€. BigQueryλŠ” μŠ€μΊ”ν•œ 데이터 μ–‘λ§ŒνΌ λΉ„μš©μ΄ λ‚˜μ˜€κΈ° λ•Œλ¬Έμ— νŒŒν‹°μ…˜ 프루닝이 μ œλŒ€λ‘œ λ˜λŠ”μ§€ ν™•μΈν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.
νŒŒν‹°μ…”λ‹ 없이
fct_orders (3μ–΅ 건 전체 μŠ€μΊ”)
-> 쿼리 느림, λΉ„μš© 많이 λ‚˜μ˜΄

νŒŒν‹°μ…”λ‹ 적용
fct_orders/
β”œβ”€β”€ date=2024-01-01/ (100만 건)
β”œβ”€β”€ date=2024-01-02/ (100만 건)
└── date=2024-01-03/ (100만 건)

-> WHERE date = '2024-01-01' 쿼리 μ‹œ
-> ν•΄λ‹Ή νŒŒν‹°μ…˜λ§Œ 읽음 -> λΉ λ₯΄κ³  μ €λ ΄

 

  • BigQuery κΈ°μ€€ νŒŒν‹°μ…”λ‹ ν…Œμ΄λΈ” 생성
CREATE TABLE fct_orders
PARTITION BY DATE(ordered_at)
AS SELECT * FROM stg_orders;

 

 

λͺ¨λ‹ˆν„°λ§ & μ•Œλ¦Ό
  • νŒŒμ΄ν”„λΌμΈμ΄ μ‹€νŒ¨ν–ˆμ„ λ•Œ 아무도 λͺ¨λ₯΄λ©΄ μ•ˆλœλ‹€.
Airflow DAG μ‹€νŒ¨
-> slack μ•Œλ¦Ό λ°œμ†‘
-> λ‹΄λ‹Ήμžκ°€ 확인
-> 원인 νŒŒμ•… ν›„ μž¬μ‹€ν–‰

 

  • Airflow μ—μ„œ Slack μ•Œλ¦Ό μ„€μ •
def alert_slack(context):
    SlackWebhookOperator(
        task_id='slack_alert',
        message=f":red_circle: νŒŒμ΄ν”„λΌμΈ μ‹€νŒ¨\nDAG: {context['dag'].dag_id}\nTask: {context['task_instance'].task_id}",
        webhook_token_conn_id='slack_webhook'
    ).execute(context)

with DAG(
    'my_pipeline',
    on_failure_callback=alert_slack,
    ...
):
    ...

 

λͺ¨λ‹ˆν„°λ§μ—μ„œ ν•¨κ»˜ 챙겨야 ν•  것듀

  • SLA : λͺ‡ μ‹œκΉŒμ§€ νŒŒμ΄ν”„λΌμΈμ΄ μ™„λ£ŒλΌμ•Ό ν•˜λŠ”κ°€
  • λ‘œκΉ… : μ–΄λŠ νƒœμŠ€ν¬μ—μ„œ μ™œ μ‹€νŒ¨ν–ˆλŠ”μ§€ 좔적 κ°€λŠ₯ν•œκ°€
  • dbt test 연동 : 데이터 ν’ˆμ§ˆ 검사 μ‹€νŒ¨λ„ μ•Œλ¦Ό λŒ€μƒμ— 포함

 

νŒŒμ΄ν”„λΌμΈ 전체 κ·Έλ¦Ό 및 섀계 체크리슀트
[MySQL: μ£Όλ¬Έ 데이터]
       ↓ Airbyte (맀일 μƒˆλ²½ 1μ‹œ μˆ˜μ§‘)
[BigQuery: Raw ν…Œμ΄λΈ”]
       ↓ dbt run (μƒˆλ²½ 1μ‹œ 30λΆ„)
[BigQuery: Marts]
       ↓ dbt test (μƒˆλ²½ 2μ‹œ)
       β”œβ”€β”€ μ‹€νŒ¨ → Slack μ•Œλ¦Ό λ°œμ†‘
       └── 성곡
[Looker λŒ€μ‹œλ³΄λ“œ κ°±μ‹  (μƒˆλ²½ 2μ‹œ 30λΆ„)]

 

β–‘ λ©±λ“±μ„±    — μž¬μ‹€ν–‰ν•΄λ„ 데이터 쀑볡이 생기지 μ•ŠλŠ”κ°€?
β–‘ νŒŒν‹°μ…”λ‹  — λ‚ μ§œ κΈ°μ€€μœΌλ‘œ 데이터λ₯Ό λ‚˜λˆ΄λŠ”κ°€?
β–‘ 재처리    — κ³Όκ±° 데이터 backfill이 κ°€λŠ₯ν•œκ°€?
β–‘ λͺ¨λ‹ˆν„°λ§  — μ‹€νŒ¨ μ‹œ μ•Œλ¦Όμ΄ μ˜€λŠ”κ°€?
β–‘ λ‘œκΉ…      — μ–΄λ””μ„œ μ‹€νŒ¨ν–ˆλŠ”μ§€ 좔적 κ°€λŠ₯ν•œκ°€?
β–‘ SLA       — λͺ‡ μ‹œκΉŒμ§€ μ™„λ£ŒλΌμ•Ό ν•˜λŠ”κ°€?