차원 모델링
고친 사람 github-actions[bot]
차원 모델링은 분석 질문에 바로 답할 수 있게 데이터를 테이블에 나눠 담습니다. 판매 한 건의 금액 같은 숫자는 테이블 하나에 모읍니다. 그 숫자를 가를 날짜나 상품 같은 기준은 테이블 여럿에 나눠 담습니다. 테이블 모양이 단순해서 집계 쿼리가 짧아집니다. 대신 같은 값을 여러 번 저장합니다.
쉽고 빠른 이해
차원 모델링은 분석용 데이터를 숫자와 그 숫자를 가를 기준으로 나눠 담는 설계입니다. 쇼핑몰이라면 판매 한 건의 수량과 금액이 숫자입니다. 언제 팔렸나와 무엇이 팔렸나가 기준입니다. 숫자를 담는 테이블을 팩트 테이블, 기준을 담는 테이블을 차원 테이블이라고 부릅니다.
서비스용 데이터베이스는 같은 값을 한 번만 저장하려고 테이블을 잘게 나눕니다. 그 모양으로 「분기별 카테고리 매출」을 물으면 테이블 여러 개를 이어 붙여야 합니다. 쿼리가 길어집니다. 어느 테이블을 이어야 하는지 찾기도 어렵습니다.
- 분석할 업무 하나를 고릅니다. 판매나 배송 같은 업무입니다
- 팩트 테이블의 한 행이 무엇인지 정합니다. 「주문에 담긴 상품 한 줄」처럼 정합니다
- 그 행을 가를 기준은 차원 테이블로, 더할 숫자는 팩트 테이블로 보냅니다
대가도 있습니다. 카테고리 이름 같은 값이 여러 번 저장됩니다. 원본 데이터를 이 모양으로 옮기는 작업을 따로 짜서 돌려야 합니다.
상세
이 절은 쇼핑몰 판매 데이터 하나를 끝까지 들고 갑니다.
정규화된 테이블로 묻는 분석 질문
서비스를 돌리는 데이터베이스는 같은 값이 두 번 저장되지 않게 테이블을 잘게 나눕니다. 이렇게 나누는 일을 정규화라고 합니다. 상품 이름을 고칠 때 한 곳만 고치면 됩니다. 그래서 주문을 넣고 고치는 일에 맞습니다.
분석 질문은 사정이 다릅니다. 「지난 분기 카테고리별 매출」을 구하려면 주문, 주문 상품, 상품, 카테고리 테이블을 차례로 이어 붙여야 합니다. 테이블 둘을 같은 번호끼리 이어 붙이는 연산이 조인입니다. 지역별로도 보려면 고객 테이블과 주소 테이블이 더 붙습니다.
아래 그림은 두 질문이 거치는 테이블을 이어진 순서대로 그렸습니다. 선 하나가 조인 하나입니다.
flowchart TD
C["카테고리"] --- P["상품"]
P --- OI["주문 상품"]
OI --- O["주문"]
O --- U["고객"]
U --- A["주소"]
조인이 늘면 쿼리가 길어집니다. 데이터베이스가 할 일도 늘어납니다. 분석하는 사람은 테이블 수십 개 가운데 무엇을 이어야 하는지부터 찾아야 합니다. 차원 모델링은 이런 질문들을 먼저 놓고 테이블을 다시 짭니다.
팩트 테이블
차원 모델은 테이블을 두 종류로 가릅니다. 첫째인 팩트 테이블은 업무에서 일어난 일을 한 행씩 적습니다. 판매라면 팔린 상품 한 줄이 한 행입니다.
팩트 테이블의 행에는 먼저 더하고 평균 낼 수 있는 숫자가 들어갑니다. 수량과 금액이 그렇습니다. 이런 숫자를 측정값이라고 부릅니다.
행에는 그 판매가 언제, 무엇을, 누구에게였는지를 가리키는 번호도 들어갑니다. 다른 테이블의 행을 번호로 가리키는 열이라 외래 키와 같은 일을 합니다. 뒤에 나오는 쿼리의 날짜_키와 상품_키가 이런 열입니다. 이 번호를 따라가면 날짜와 상품과 고객의 자세한 값이 나옵니다.
팩트 테이블은 행이 가장 많은 테이블입니다. 판매가 일어날 때마다 한 행씩 늘어나서 다른 테이블보다 몇 자릿수 커집니다. 그래서 행 하나는 측정값과 외래 키만 담아 가늘게 둡니다.
차원 테이블
둘째인 차원 테이블은 측정값을 가를 기준을 담습니다. 날짜 차원, 상품 차원, 고객 차원이 각각 테이블 하나입니다. 질문에 「카테고리별로」나 「지역별로」가 나오면 그 기준은 차원 테이블의 열에 있습니다.
차원 테이블은 옆으로 넓습니다. 상품 차원 한 행에는 상품 이름, 브랜드, 카테고리, 상위 카테고리가 다 들어 있습니다. 고객 차원 한 행에는 이름과 지역과 회원 등급이 들어 있습니다. 정규화된 쪽이라면 카테고리나 지역은 따로 떼어 냈을 값입니다.
떼어 낼 수 있는 값을 일부러 한 테이블에 모으는 일을 반정규화라고 합니다. 같은 카테고리 이름이 상품 수만큼 되풀이됩니다. 대신 상품의 어떤 기준으로 묶든 조인 하나로 닿습니다.
날짜도 차원 테이블 하나가 됩니다. 한 행이 하루입니다. 열로는 연, 분기, 월, 요일, 공휴일 여부를 가집니다. 「주말 매출」이나 「공휴일 매출」을 날짜 함수 없이 열 하나로 거를 수 있습니다.
스타 스키마
팩트 테이블 하나를 가운데 두고 차원 테이블 여럿이 둘러싸면 별 모양이 됩니다. 이 모양을 스타 스키마라고 부릅니다. 아래 그림은 판매 팩트 테이블과 차원 테이블 셋을 그렸습니다.
flowchart TD
D1["날짜 차원<br/>연 · 분기 · 월 · 요일"] --- F["판매 팩트 테이블<br/>수량 · 금액 · 외래 키 셋"]
D2["상품 차원<br/>이름 · 브랜드 · 카테고리"] --- F
F --- D3["고객 차원<br/>이름 · 지역 · 회원 등급"]
이 모양에서는 분석 질문 하나가 팩트 테이블과 차원 몇 개의 조인으로 끝납니다. 「3월 카테고리별 매출」은 아래 쿼리 하나입니다.
SELECT p.카테고리, SUM(f.금액)
FROM 판매 f
JOIN 날짜 d ON f.날짜_키 = d.날짜_키
JOIN 상품 p ON f.상품_키 = p.상품_키
WHERE d.월 = 3
GROUP BY p.카테고리;
조인은 질문에 나온 차원 수만큼만 붙습니다. 지역별로도 보려면 고객 차원 하나를 더 붙이면 됩니다. 정규화된 쪽에서 고객과 주소 테이블을 차례로 잇던 일이 조인 하나로 줄어듭니다.
차원 테이블을 다시 정규화해 카테고리를 따로 떼어 내면 별의 끝에 가지가 뻗습니다. 이 모양은 스노우플레이크 스키마라고 부릅니다. 아래 그림은 상품 차원에서 카테고리가 떨어져 나간 모양입니다.
flowchart TD
D["날짜 차원 · 고객 차원"] --- F["판매 팩트 테이블"]
F --- P["상품 차원<br/>이름 · 브랜드"]
P --- C["카테고리 차원<br/>카테고리 · 상위 카테고리"]
카테고리 이름은 이제 한 번만 저장됩니다. 대신 카테고리별 매출을 구하려면 조인이 하나 더 붙습니다.
그레인
팩트 테이블을 짤 때 가장 먼저 정하는 것은 한 행이 무엇을 뜻하는가입니다. 이것을 그레인(grain)이라고 부릅니다. 「주문 한 건」과 「주문에 담긴 상품 한 줄」은 서로 다른 그레인입니다.
그레인이 흐리면 합계가 틀립니다. 주문 한 건 단위의 행과 상품 한 줄 단위의 행이 한 테이블에 섞이면 같은 판매가 두 번 더해집니다. 그래서 팩트 테이블 하나에는 그레인이 하나만 있어야 합니다.
그레인을 잘게 잡을수록 나중에 할 수 있는 질문이 많아집니다. 상품 한 줄 단위로 두면 주문 한 건의 합계는 더해서 만들 수 있습니다. 반대로 하루 합계 단위로만 두면 상품별 매출은 다시 만들 수 없습니다.
flowchart TD
L["상품 한 줄"] -->|더해서 만든다| O["주문 한 건"]
O -->|더해서 만든다| D["하루 합계"]
D -.- N["거꾸로는 다시 만들 수 없다"]
설계 네 단계
차원 모델 하나는 흔히 네 단계로 짭니다. 그레인이 차원과 측정값보다 앞에 온다는 순서가 중요합니다. 그레인이 정해져야 어떤 기준과 숫자가 그 행에 맞는지 가릴 수 있습니다.
- 업무 프로세스를 하나 고릅니다. 판매, 재고, 배송처럼 일어날 때마다 기록이 남는 업무입니다
- 그레인을 선언합니다. 팩트 테이블 한 행이 무엇인지 한 문장으로 적습니다
- 차원을 고릅니다. 그 행을 언제, 무엇, 누가, 어디서로 가를 기준을 찾습니다
- 측정값을 고릅니다. 그 그레인에서 더할 수 있는 숫자를 찾습니다
대리 키
차원 테이블의 행에는 차원 모델 쪽에서 새로 매긴 번호를 붙입니다. 원본 시스템의 상품 번호를 쓰지 않고 1, 2, 3 처럼 따로 매깁니다. 이 번호를 대리 키(surrogate key)라고 부릅니다.
팩트 테이블의 외래 키 열에는 이 대리 키가 들어갑니다. 앞 쿼리의 상품_키는 상품 차원의 대리 키를 담은 열입니다. 원본 시스템이 쓰던 번호는 자연 키라고 합니다.
따로 매기는 까닭은 둘입니다. 여러 원본 시스템이 같은 번호를 서로 다른 상품에 쓰고 있을 수 있습니다. 그리고 한 고객의 예전 모습과 지금 모습을 두 행으로 나눠 둘 때 두 행이 서로 다른 번호를 가져야 합니다. 뒤의 까닭은 다음 소절에서 봅니다.
천천히 변하는 차원
차원의 값은 가끔 바뀝니다. 고객이 서울에서 부산으로 이사하는 경우가 그렇습니다. 이렇게 드물게 값이 바뀌는 차원을 천천히 변하는 차원(slowly changing dimension)이라고 부릅니다.
바뀐 값을 다루는 방법은 크게 둘입니다. 첫째는 기존 행을 덮어쓰는 것입니다. 이사 전 판매 행도 지역만 바뀐 그 행을 가리킵니다. 그래서 이사 전 판매까지 부산 판매로 집계됩니다.
둘째로 새 행을 하나 더 만들어 새 대리 키를 줄 수도 있습니다. 그러면 이사 전 판매는 서울로, 이사 뒤 판매는 부산으로 남습니다.
아래 그림은 두 방법에서 판매 행이 어느 고객 행을 가리키는지 그렸습니다. 고객 차원의 대리 키는 7 과 12 로 잡았습니다.
flowchart TD
subgraph W["덮어쓰기"]
S1["이사 전 판매"] --> C7["고객 차원 키 7 · 부산"]
S2["이사 뒤 판매"] --> C7
end
subgraph N["새 행 추가"]
T1["이사 전 판매"] --> K7["고객 차원 키 7 · 서울"]
T2["이사 뒤 판매"] --> K12["고객 차원 키 12 · 부산"]
end
C7 ~~~ T1
새 행 추가에서 이사 전 판매 행은 옛 행의 키 7 을 그대로 가리킵니다. 옛 팩트 행은 옛 대리 키를 가리킨 채 남는 것입니다. 어느 쪽을 고를지는 과거를 그때 모습으로 봐야 하는지에 달렸습니다.
컨폼드 디멘션
팩트 테이블은 업무 프로세스마다 하나씩 생깁니다. 판매 팩트와 재고 팩트가 따로 있으면 팔린 양과 남은 양을 나란히 보고 싶어집니다. 두 팩트 테이블이 같은 상품 차원과 날짜 차원을 가리켜야 이 비교가 됩니다.
아래 그림은 두 팩트 테이블이 차원을 함께 가리키는 모양입니다. 고객 차원은 판매에만 붙습니다.
flowchart TD
S["판매 팩트 테이블"] --- P["상품 차원"]
S --- D["날짜 차원"]
S --- U["고객 차원"]
I["재고 팩트 테이블"] --- P
I --- D
여러 팩트 테이블이 함께 쓰는 차원을 컨폼드 디멘션(conformed dimension)이라고 부릅니다. 우리말로 공통 차원이라고도 합니다. 컨폼드 디멘션을 두면 업무 하나씩 차원 모델을 늘려 가도 모델끼리 이어집니다.
차원 모델이 내주는 것
차원 모델은 읽기 편한 대신 세 가지를 내줍니다.
첫째, 같은 값이 여러 번 저장됩니다. 카테고리 이름을 고치려면 그 카테고리에 든 상품 행을 전부 고쳐야 합니다. 쓰기가 잦은 서비스용 데이터베이스에 이 모양을 쓰지 않는 까닭입니다.
둘째, 원본 데이터를 이 모양으로 옮기는 작업이 따로 필요합니다. 원본 테이블을 읽어 팩트와 차원으로 나누고 대리 키를 붙이는 일입니다. 이 작업을 흔히 ETL(Extract-Transform-Load, 추출-변환-적재)이라고 부릅니다.
ETL 은 정해진 주기로 돌립니다. 그래서 차원 모델의 데이터는 원본보다 늦습니다.
셋째, 그레인과 차원은 미리 떠올린 질문에 맞춰져 있습니다. 하루 단위로 요약해 둔 팩트 테이블에는 시간대별 매출을 물을 수 없습니다. 새 질문이 기존 그레인보다 잘게 들어가면 팩트 테이블을 다시 짜야 합니다.
정규화 모델과 견준 모습
서비스용 테이블을 짜는 방법은 흔히 엔티티-관계 모델링이라고 부릅니다. 업무에 나오는 대상과 그 사이의 관계를 테이블로 옮깁니다. 그 테이블을 정규화합니다. 두 모델을 몇 가지 축으로 나란히 놓으면 아래와 같습니다.
| 정규화된 서비스용 모델 | 차원 모델 | |
|---|---|---|
| 맞춘 일 | 주문 한 건을 넣고 고치기 | 많은 행을 훑어 집계하기 |
| 같은 값 | 한 곳에만 둔다 | 여러 곳에 되풀이한다 |
| 테이블 모양 | 잘게 나뉜 테이블 여럿 | 팩트 테이블 하나와 차원 테이블 몇 개 |
| 분석 질문 하나의 조인 | 많다 | 쓰는 차원 수만큼 |
| 데이터가 들어오는 길 | 애플리케이션이 바로 쓴다 | ETL 이 주기적으로 싣는다 |
두 모델을 따로 두는 까닭은 표 첫 줄의 맞춘 일이 달라서입니다. 서비스용 데이터베이스는 쓰기에 맞춰 둡니다. 분석용 데이터는 차원 모델로 옮겨서 읽기에 맞춥니다.
쓰는 곳과 안 쓰는 곳
차원 모델은 데이터 웨어하우스와 데이터 마트에서 씁니다. 여러 시스템의 데이터를 모아 두고 분석 질문에 답하는 저장소들입니다. 대시보드와 리포트가 읽는 테이블이 이 모양일 때가 많습니다.
주문을 받고 재고를 깎는 서비스용 데이터베이스에는 쓰지 않습니다. 쓰기가 잦고 값이 한 곳에만 있어야 하는 곳입니다. 그래서 정규화 모델이 맞습니다.
분석할 질문이 아직 없는 원본 데이터를 쌓아 둘 때도 쓰지 않습니다. 그런 데이터는 데이터 레이크에 먼저 둡니다. 질문이 정해지면 그때 차원 모델로 옮깁니다.
웨어하우스 전체를 차원 모델로 지을지는 두 방식으로 갈립니다. 아래 그림은 원본 데이터가 두 방식에서 무엇을 거쳐 가는지 그렸습니다.
flowchart TD
subgraph K["Kimball 방식"]
KA["원본 시스템"] --> KS["판매 차원 모델"]
KA --> KI["재고 차원 모델"]
KS --- KC["컨폼드 디멘션<br/>상품 · 날짜"]
KI --- KC
end
subgraph I["Inmon 방식"]
IA["원본 시스템"] --> IW["웨어하우스 한가운데<br/>정규화 모델"]
IW --> IM1["판매 데이터 마트<br/>차원 모델"]
IW --> IM2["재고 데이터 마트<br/>차원 모델"]
end
KC ~~~ IA
Ralph Kimball 이 널리 알린 방식은 웨어하우스 전체를 차원 모델로 짓습니다. 판매 차원 모델과 재고 차원 모델이 컨폼드 디멘션으로 이어져 한 웨어하우스가 됩니다.
데이터 마트는 웨어하우스에서 부서 하나가 쓸 몫만 떼어 낸 저장소입니다. Bill Inmon 의 Corporate Information Factory 는 웨어하우스 한가운데를 정규화 모델로 둡니다. 차원 모델로 짓는 것은 그 아래 데이터 마트뿐입니다.
관련 항목
차원 모델을 이루는 구성 요소
팩트 테이블 · 차원 테이블 · 그레인 · 측정값 · 대리 키 · 자연 키 · 외래 키
차원 모델이 만드는 스키마 모양
스타 스키마 · 스노우플레이크 스키마 · 팩트 컨스텔레이션
차원 테이블을 다루는 기법
천천히 변하는 차원 · 컨폼드 디멘션 · 반정규화 · 정크 디멘션 · 역할 수행 차원 · 퇴화 차원 · 브리지 테이블
차원 모델과 맞세워지는 설계 방법
정규화 · 엔티티-관계 모델링 · 데이터 볼트 · 원 빅 테이블
차원 모델로 웨어하우스를 짓는 방식
엔터프라이즈 데이터 웨어하우스 버스 아키텍처 · Corporate Information Factory · 메달리온 아키텍처
차원 모델을 담는 분석 저장소
데이터 웨어하우스 · 데이터 마트 · 레이크하우스 · 열 지향 저장
차원 모델을 읽는 분석 도구
OLAP · OLAP 큐브 · 비즈니스 인텔리전스 · 시맨틱 레이어
차원 모델로 데이터를 옮겨 오는 처리와 원천
다른 이름: dimensional modeling · dimensional modelling · 디멘셔널 모델링 · 차원 모델