988 words
5 minutes
[DE Design Pattern]08-02. Vertial Partitionor
2. Partitioning — Vertical Partitioner
01. Pattern Overview
- 수직 파티셔닝은 하나의 행(row)을 속성(column) 기준으로 분할하여 서로 다른 저장소(테이블, 디렉토리)에 나누어 저장하는 패턴.
- 수평 파티셔닝이 행 전체를 다른 위치로 옮기는 것과 대비된다.
핵심 아이디어는 관련 속성끼리 묶어서 분리 저장하는 것
- mutable 속성 (방문 시간, 방문 페이지) → 한 테이블
- immutable 속성 (IP 주소, 디바이스 정보) → 별도 테이블
이렇게 나누면 immutable 데이터가 매 방문마다 중복 저장되는 것을 방지
02. 구현 프로세스
- 데이터 분류(Classification): 관련 속성을 그룹으로 합침 (예: mutable vs immutable)
- 결합 키(Join Key) 식별: 분리된 테이블을 다시 합칠 때 사용할 공통 키 설정 (예:
visit_id) - 분리 저장: 각 그룹을 별도 테이블/디렉토리에 기록
03. 수평 vs 수직 파티셔닝 비교
- 수평 파티셔닝에서는 행 전체가 파티션 키 값에 따라 다른 위치로 이동됨.
- 수직 파티셔닝에서는 한 행의 컬럼들이 분리되어 각각 다른 테이블에 저장되고,
visit_id같은 공통 키로 연결
04. 특성
- 스토리지 절약: immutable 속성의 중복 저장 방지
- 유연한 정책 적용: 분리된 테이블마다 다른 retention, access policy 적용 가능
- 업데이트 효율: mutable 속성만 있는 테이블은 업데이트 범위가 작아짐
05. Consequences
Domain Split: 논리적으로 관련된 속성이 물리적으로 다른 곳에 저장되므로 데이터를 이해하기 어려워짐. 좋은 문서화가 필수
쿼리 복잡성: 전체 행을 보려면 JOIN이 필요하다. Dataset Materializer 패턴으로 뷰를 만들어 완화.
Producer 부담: 데이터를 쓰는 쪽에서 행 분할 로직을 구현필요. 여러 테이블에 동시에 써야 하므로 네트워크 비용이 증가
Spark에서 수직 파티셔닝
from pyspark.sql import SparkSession, functions as F
spark = SparkSession.builder.getOrCreate()
visit_schema = """ visit_id STRING, event_time TIMESTAMP, page STRING, context STRUCT user: STRUCT<login STRING, ip STRING>, technical: STRUCT<browser STRING, browser_version STRING, os STRING> >"""visits = spark.read.schema(visit_schema).json("/data/visits")
# 중요: 입력 데이터를 2번 읽지 않도록 persistvisits.persist()
# 1) 방문 이벤트 테이블 (user/technical context 제거)visits_events = ( visits .drop("context") .select("visit_id", "event_time", "page"))visits_events.write.format("delta").save("/output/visits_events")
# 2) 사용자 컨텍스트 테이블user_context = visits.selectExpr("visit_id", "context.user.*")user_context.write.format("delta").save("/output/visits_user_context")
# 3) 기술 컨텍스트 테이블tech_context = visits.selectExpr("visit_id", "context.technical.*")tech_context.write.format("delta").save("/output/visits_tech_context")
visits.unpersist()- persist를 호출하지 않으면 Spark가 원본 데이터를 테이블 수만큼 반복해서 읽게 되어 I/O가 배로 늘어남
- 분리 저장 후에는
unpersist()로 메모리를 해제
SQL 예제: PostgreSQL CTAS
-- 기술 컨텍스트만 별도 테이블로 분리 (CTAS)CREATE TABLE dedp.technical_context ASSELECT DISTINCT visit_id, context->'technical'->>'browser' AS browser, context->'technical'->>'browser_version' AS browser_version, context->'technical'->>'os' AS osFROM dedp.visits_all;Concept
- Vertical Partitioner : 하나의 행을 속성(column) 기준으로 분할하여 서로 다른 테이블/디렉토리에 저장하는 패턴
- Data Classification : 수직 파티셔닝의 첫 단계. 관련 속성을 그룹으로 묶는 작업 (예: mutable vs immutable)
- Join Key : 분리된 테이블을 다시 결합할 때 사용하는 공통 키 (예: visit_id)
- persist() : Spark에서 동일 데이터셋을 여러 번 사용할 때 메모리에 캐시하여 중복 읽기를 방지하는 함수
- Domain Split : 수직 파티셔닝의 부작용. 논리적으로 관련된 데이터가 물리적으로 분리되어 이해가 어려워지는 현상
- CTAS (Create Table As Select) : SELECT 쿼리 결과를 바로 새 테이블로 생성하는 SQL 구문
- INSERT INTO … SELECT FROM : 기존 테이블에 SELECT 결과를 삽입하는 SQL 구문
- Mutable/Immutable 속성 분리 : 변경 빈도가 다른 속성을 분리하여 업데이트 범위와 스토리지 중복을 줄이는 전략
[DE Design Pattern]08-02. Vertial Partitionor
https://yjinheon.netlify.app/posts/02de/00-de-design-pattern/08_data_storage/08-02-vertical_partitionor/