이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Data quality dimension은 품질 이상을 어떤 언어로 분류하게 해주는가?
  • freshness, completeness, accuracy, validity, consistency는 각각 어떤 장애를 가리키는가?
  • 품질 차원을 나누면 원인 분석과 재처리 범위가 어떻게 달라지는가?
  • 개인 프로젝트에서 최소한 어떤 품질 차원부터 검증해야 하는가?

개요

data quality dimension은 데이터 이상을 분류하는 공통 언어다.

“대시보드 숫자가 이상하다”는 말만으로는 원인을 찾기 어렵다.

최신성이 늦은 것인지, row가 빠진 것인지, 값이 틀린 것인지, schema가 깨진 것인지 구분해야 한다.

dimension을 나누면 alert, owner, runbook도 구체화된다.

주요 차원

차원질문예시
Freshness최신 데이터인가?마지막 주문 event가 30분 넘게 갱신되지 않음
Completeness빠진 데이터가 없는가?source 10만 건 중 mart 8만 건
Accuracy값이 맞는가?매출 합계가 source와 다름
Validity형식과 범위가 유효한가?음수 결제 금액, 잘못된 enum
Consistency서로 다른 테이블이 모순되지 않는가?주문 상태는 PAID인데 결제 fact 없음

모든 rule을 한 번에 만들 필요는 없다.

하지만 어떤 차원을 보고 있는지 이름을 붙여야 한다.

Freshness

freshness는 데이터가 얼마나 최신인지 보는 차원이다.

SELECT max(event_time) AS latest_event_time
FROM mart_order_events;

최신 시각이 현재보다 30분 이상 늦으면 stale로 볼 수 있다.

freshness 문제는 source 지연, pipeline 실패, scheduler 지연, consumer lag에서 온다.

freshness가 실패했다고 값이 틀렸다고 단정하면 안 된다.

Completeness

completeness는 빠진 row나 partition이 없는지 본다.

SELECT source.cnt AS source_count,
       mart.cnt AS mart_count
FROM (SELECT count(*) cnt FROM orders WHERE created_at >= current_date) source
CROSS JOIN (SELECT count(*) cnt FROM mart_orders WHERE order_date = current_date) mart;

count 차이가 크면 누락 가능성이 있다.

단, source와 mart의 grain이 다르면 단순 count 비교가 틀릴 수 있다.

completeness rule은 grain을 함께 문서화해야 한다.

Accuracy

accuracy는 값이 맞는지 본다.

SELECT source.amount_sum, mart.amount_sum
FROM (
  SELECT sum(paid_amount) amount_sum FROM orders WHERE order_date = current_date
) source
CROSS JOIN (
  SELECT sum(paid_amount) amount_sum FROM mart_order_daily WHERE order_date = current_date
) mart;

count가 맞아도 amount가 틀릴 수 있다.

accuracy 문제는 transform logic, currency, timezone, refund 처리에서 자주 생긴다.

Validity와 Consistency

validity는 값의 형식과 범위를 본다.

SELECT count(*)
FROM payments
WHERE amount < 0 OR status NOT IN ('READY', 'PAID', 'CANCELED');

consistency는 여러 데이터 사이의 모순을 본다.

SELECT count(*)
FROM orders o
LEFT JOIN payments p ON p.order_id = o.id
WHERE o.status = 'PAID' AND p.id IS NULL;

이 두 차원은 백엔드 domain rule과 밀접하다.

데이터 팀이 rule을 만들려면 백엔드가 상태 의미를 설명해야 한다.

원인 분리

dimension을 나누면 원인 분석이 빨라진다.

freshness 실패는 scheduler, consumer, source arrival을 먼저 본다.

completeness 실패는 range, join, filter, partition 누락을 본다.

accuracy 실패는 transform logic과 business rule 변경을 본다.

validity 실패는 schema 계약과 producer validation을 본다.

consistency 실패는 여러 source 간 transaction/event 경계를 본다.

운영 지표

  • freshness lag: 최신성 지연이다.
  • completeness ratio: source 대비 target 비율이다.
  • accuracy diff amount: 금액 차이다.
  • invalid row count: validity 실패 row 수다.
  • consistency violation count: 관계 모순 수다.
  • rule failure count by dimension: 차원별 실패 수다.

dimension label이 있어야 장애 추이를 볼 수 있다.

모든 품질 실패를 하나의 quality_failed로만 세면 개선할 지점을 찾기 어렵다.

개인 프로젝트 기준

개인 프로젝트에서는 세 가지부터 시작한다.

  • freshness: 최신 event time이 기준 안에 있는가
  • completeness: source와 target count가 맞는가
  • accuracy: source와 target sum이 맞는가

이후 null ratio, enum validity, status consistency를 추가한다.

rule 결과를 table에 남기면 회고와 알림으로 확장하기 쉽다.

위험 신호!

  • “데이터 품질”을 하나의 boolean으로만 본다.
  • freshness만 보고 completeness와 accuracy를 보지 않는다.
  • source와 target grain이 다른데 count만 비교한다.
  • validity rule이 domain enum 변경을 따라가지 못한다.
  • 품질 실패에 owner와 severity가 없다.

확인 질문

확인 질문

  • 이 품질 이상은 freshness, completeness, accuracy 중 어디에 가까운가?
    • 차원에 따라 볼 지표와 owner가 달라진다.
  • source와 target의 grain은 같은가?
    • completeness와 accuracy rule 전에 반드시 확인해야 한다.
  • 이 rule은 어떤 business 의미를 검증하는가?
    • 단순 SQL이 아니라 domain rule과 연결되어야 한다.

참고 문서