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. 구현 프로세스#

  1. 데이터 분류(Classification): 관련 속성을 그룹으로 합침 (예: mutable vs immutable)
  2. 결합 키(Join Key) 식별: 분리된 테이블을 다시 합칠 때 사용할 공통 키 설정 (예: visit_id)
  3. 분리 저장: 각 그룹을 별도 테이블/디렉토리에 기록

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번 읽지 않도록 persist
visits.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 AS
SELECT DISTINCT
visit_id,
context->'technical'->>'browser' AS browser,
context->'technical'->>'browser_version' AS browser_version,
context->'technical'->>'os' AS os
FROM 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/
Author
Datamind
Published at
2025-03-27
License
CC BY-NC-SA 4.0