본문으로 건너뛰기

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

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

개념​

  • 부모 테이블 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 미만은 단일 테이블과 인덱스로 충분한 경우가 많고, 50~200GB는 보존 정책이나 인덱스 관리 부담이 있을 때 검토하며, 200GB를 넘으면 대부분 파티셔닝을 고려합니다. 이 경계값은 공식 문서에 있는 기준이 아니라 경험적 출발점입니다. 실제 판단은 크기보다 보존 정책과 쿼리 패턴이 좌우합니다.

보존 기간이 90일, 1년처럼 명확하면 만료 데이터를 파티션 단위로 한 번에 제거할 수 있어 파티셔닝의 이점이 가장 분명합니다. 반대로 insert와 lookup이 모두 무작위에 가까우면 파티셔닝 효과가 작습니다. 글로벌 unique 제약이 꼭 필요하다면 제약에 파티션 키를 추가할 수 있는지부터 신중히 검토해야 합니다.

조건별로 어울리는 파티션 타입은 다음과 같습니다.

조건파티션 타입
시간 기반 쿼리가 대부분range
tenant 별 격리 + 쿼리list 또는 hash
단순 분산 부하hash

테이블 크기와 운영 조건에 따른 파티셔닝 도입 판단

파티션 키 선택​

키는 한 번 정하면 바꾸기 어렵습니다. 파티션 키를 바꾸는 일은 곧 테이블 재구성이므로 처음에 신중히 정해야 합니다.

좋은 키의 조건:

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

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

파티션 타입 미리보기​

RANGE·LIST·HASH 세 종류이며, 각각의 동작은 7.2에서 다룹니다.

타입분할 기준사용처
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) 없이는 운영 부담이 커 사실상 도입이 어려움