본문으로 건너뛰기
7.1 파티셔닝 개요와 결정 기준

7.1 파티셔닝 개요와 결정 기준

테이블 한 개가 수억~수십억 row로 자라면 단일 테이블로는 운영이 어려워집니다. 인덱스 BLOAT, VACUUM 시간, 백업·복구, 보존 정책 적용 — 모두 비용이 폭증합니다. 파티셔닝(partitioning)은 큰 테이블을 작은 자식 테이블 여러 개로 물리적으로 나누고, 사용자에게는 하나의 테이블처럼 보이게 합니다. 파티셔닝의 개념과 도입할지 말지의 결정 기준을 정리합니다.

개념

    flowchart TD
  P["events (parent)<br/>partitioned by RANGE(created_at)"]
  C1["events_2025_q4<br/>(2025-10 ~ 2026-01)"]
  C2["events_2026_q1<br/>(2026-01 ~ 2026-04)"]
  C3["events_2026_q2<br/>(2026-04 ~ 2026-07)"]
  P --> C1
  P --> C2
  P --> C3

  classDef parent fill:#ede9fe,stroke:#6d28d9,color:#3b0764,stroke-width:2px
  classDef child fill:#d1fae5,stroke:#047857,color:#064e3b
  class P parent
  class C1,C2,C3 child
  
  • 부모 테이블 events는 레코드를 직접 저장하지 않는 경우가 많다 (declarative partition)
  • INSERT/SELECT는 부모 이름으로 — PostgreSQL이 알아서 자식 테이블로 라우팅
  • 각 자식은 독립적인 테이블 — 자체 인덱스, 자체 VACUUM, 자체 통계

무엇이 좋아지나

영역효과
쿼리 성능WHERE created_at >= '2026-04-01'은 한 파티션만 스캔 (partition pruning)
VACUUM·인덱스파티션 단위로 vacuum·reindex 가능. 한 파티션 BLOAT가 다른 파티션 영향 없음
데이터 보존 정책오래된 파티션을 DETACH PARTITION + DROP으로 한 번에 제거
백업파티션 단위로 백업·복원 가능 (특히 cold 파티션은 read-only로 분리)
인덱스 빌드큰 인덱스를 파티션 단위로 작은 인덱스 여러 개로 → 더 빠르고 BLOAT 덜 위험
통계파티션별 통계로 옵티마이저 정확도 ↑

무엇이 안 좋아지나

측면단점
글로벌 unique constraint파티션 키를 포함해야 unique 가능. (id, partition_key)로 보내는 식
외래 키파티션된 부모를 가리키는 FK는 PG 12+에서 지원 — 그 전엔 불가
partition pruning이 안 되는 쿼리WHERE func(created_at) > ... 같은 표현식이면 모든 파티션 스캔
파티션 수가 너무 많음100+ 파티션이면 plan 시간이 커지고 카탈로그 비대
운영 복잡도파티션 생성·정리·이름 규칙·자동화 필요
변경 SQL의 락부모에 ALTER TABLE은 모든 자식에 영향

도입할까 말까 — 결정 기준

조건판단
테이블 크기 < 50GB파티셔닝 안 함 — 단일 테이블 + 인덱스로 충분
50GB ~ 200GB보존 정책·인덱스 관리 부담이 있으면 검토
200GB+거의 항상 파티셔닝
보존 기간이 명확 (90일·1년 등)파티셔닝이 정석 — 만료 데이터 한 번에 제거
시간 기반 쿼리가 대부분range 파티션
tenant 별 격리 + 쿼리list 또는 hash 파티션
단순 분산 부하hash 파티션
매우 random insert + lookup파티셔닝 효과 작음
글로벌 unique 제약 필수신중히 검토 — 파티션 키 추가 가능한지

파티션 키 선택

키 선택은 한 번 정하면 바꾸기 어렵다(파티션 키 변경 = 테이블 재구성). 신중히 진행합니다.

좋은 키의 조건:

  • 쿼리 WHERE 절에 자주 등장
  • 시간순 등 자연 분포가 있음
  • NOT NULL (NULL은 DEFAULT 파티션이 필요)
  • 변경되지 않음 (UPDATE로 파티션 이동 가능하지만 비용 큼)

시계열 데이터는 created_at 또는 event_date 가 표준입니다. 멀티 테넌트는 tenant_id, 분산은 id % N(hash).

파티션 타입 미리보기

세 종류 — 자세한 동작은 다음 절.

타입분할 기준사용처
RANGE값의 범위시계열·연속값
LIST값의 명시적 목록tenant·국가·카테고리
HASH해시값 모듈로부하 분산

파티션 단위 결정 — 너무 많지도 적지도

단위권장 시나리오
매일 보존·삭제. 데이터량 작거나 1년치만 보유
1~2년치 데이터
가장 흔한 — 1~5년 데이터
분기5년+ 장기 보유
매우 cold한 데이터

너무 잘게 자르면 파티션 수 폭증 → 플래너 비용·카탈로그 비대. 너무 듬성하면 보존 단위가 크고 한 파티션이 너무 큽니다.

경험치: 한 테이블에 활성 파티션 50개 이하가 가장 운영하기 좋습니다. 데이터 보유 5년 + 월 파티션 = 60개로 약간 넘는다 — 오래된 건 분기로 합치는 패턴(rolling).

운영 자동화의 필수성

파티션 자체를 만드는 건 쉽지만, 매일·매월 새 파티션을 만들고 오래된 파티션을 정리하는 일을 사람이 할 수는 없습니다. 자동화 도구:

  • pg_partman: 표준입니다. 시간 기반 자동 생성·삭제·archive. 7.5에서 자세히
  • 자체 cron + SQL: 작은 시스템에서 가능
  • Citus/TimescaleDB: 시계열 특화 확장이 자체 관리

정리

  • 파티셔닝 = 큰 테이블을 작은 자식 여러 개로 물리 분할
  • 50GB 이하면 불필요, 200GB+면 거의 필수
  • 시계열·보존 정책·VACUUM 부담이 자주 도입 사유
  • 파티션 키는 NOT NULL · 자주 WHERE에 등장 · 변경 없음 조건
  • 단위는 보통 월. 활성 파티션 50개 이하 권장
  • 자동화(pg_partman) 없이는 운영 부담이 커서 사실상 도입 안 됨

다음 절(7.2)에서는 세 가지 파티션 타입 — RANGE / LIST / HASH — 의 동작을 봅니다.