본문으로 건너뛰기
13.3 파티셔닝을 활용한 튜닝

13.3 파티셔닝을 활용한 튜닝

Part VII에서 파티셔닝의 기본 동작을 봤다면, 파티셔닝이 성능을 어떻게 만들어내는지와 운영 튜닝 포인트. 큰 테이블의 마지막 무기이지만 잘못 쓰면 오히려 손해.

파티셔닝이 성능에 영향을 주는 4가지

영역효과
partition pruningWHERE에 맞는 파티션만 스캔 — 작은 인덱스·작은 데이터
partition-wise join같은 파티션끼리 join — 메모리·CPU 효율
partition-wise aggregate파티션 단위 부분 집계 → 합산
인덱스 분할큰 인덱스 1개 → 작은 인덱스 N개. BLOAT 분리, REINDEX 빠름

partition pruning 활용

7.4에서 본 가지치기. 효과를 살리는 쿼리 패턴:

좋음나쁨
WHERE created_at >= '2026-05-01'WHERE date_trunc('day', created_at) = ...
WHERE id BETWEEN 1000 AND 2000 (range 파티션)WHERE id IN (SELECT id FROM ...) (서브쿼리)
WHERE country = 'KR' (list 파티션)WHERE country IN (SELECT ...)

ORM이 자동 생성하는 쿼리가 cast나 함수 호출을 끼우면 pruning 무력화 — 쿼리 패턴 점검합니다.

plan-time vs execution-time

EXPLAIN <쿼리>
-- "Subplans Removed: N" → execution-time pruning
-- Append 아래 자식 1개만 → plan-time pruning (더 좋음)

prepared statement의 generic plan은 모든 파티션을 plan에 포함해 plan time 폭증합니다. 운영 권장:

  • 매우 많은 파티션 + 자주 호출되는 쿼리 → plan_cache_mode = force_custom_plan

partition-wise join

SET enable_partitionwise_join = on;     -- PG 14+ 기본 on

SELECT *
  FROM orders o
  JOIN order_items i ON i.order_id = o.id AND i.created_at = o.created_at;
-- 양쪽이 같은 키로 파티션됐다면 — 파티션 단위 join

조건:

  • 두 테이블이 같은 키로 파티션
  • 파티션 경계 완전히 일치
  • enable_partitionwise_join = on

효과: 큰 hash table을 만들지 않고 작은 join 여러 개. 메모리·시간 모두 절약합니다.

partition-wise aggregate

SET enable_partitionwise_aggregate = on;

SELECT user_id, count(*)
  FROM events
 GROUP BY user_id;
-- 파티션별 GROUP BY → 합산

효과: parallel scan과 결합되면 매우 큰 데이터셋에서 강력. PG 14+ 기본 on.

파티션 개수 — 균형

파티션 수영향
적음 (< 20)plan 비용 작음. 각 파티션이 큼
중간 (20~100)균형. 운영 권장
많음 (100~500)plan time ↑. relcache 비대
매우 많음 (1000+)plan time 폭증, autovacuum 부담

운영 권장: 활성 파티션 50개 이하. 오래된 파티션은 분기·년 단위로 합침(7.5).

파티션 키 선택

좋은 키:

  • WHERE에 항상 등장
  • selectivity 효과 큼
  • NOT NULL
  • 변경되지 않음

시계열은 created_at 또는 event_date. 멀티 테넌트는 tenant_id. 분산은 hash.

키가 쿼리 패턴에 없으면 pruning 효과 없음 — 큰 결정 실수.

파티션 인덱스 전략

-- 부모에 만들면 모든 자식에 자동 생성
CREATE INDEX ON events (user_id);

-- 특정 파티션에만 — 일반적이지 않지만 가능
CREATE INDEX ON events_2026_05 (extra_col);
패턴메모
부모에 만들기일관성, 자동
자식별 다른 인덱스분석 파티션만 추가 — 가능
unique 인덱스파티션 키 포함 필수

인덱스 빌드 — 파티션 단위로 빠름

큰 인덱스를 한 번에 만들면 수 시간 걸립니다. 파티션 단위는 각 파티션이 작아 빠르고, 병렬도 가능합니다.

# 자식 파티션마다 CONCURRENTLY로 빌드
for part in $(psql -At -c "SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent='events'::regclass"); do
  psql -c "CREATE INDEX CONCURRENTLY ON $part (user_id);"
done

# 마지막에 부모에 명시적 — 자식이 이미 있으면 attach만
CREATE INDEX ON events (user_id);   -- 일관성 검사 + attach

PG 12+의 CREATE INDEX ... ON ONLY parent로 부모만 만들고 자식은 따로 ATTACH도 가능합니다.

VACUUM·ANALYZE 분리

각 파티션은 독립 통계·BLOAT. 자동 autovacuum이 자식별로 동작합니다.

-- 명시 ANALYZE
ANALYZE events_2026_05;

-- 부모 통계 (PG 14+)
ANALYZE ROOT events;

큰 부모 ANALYZE는 모든 자식 샘플링으로 시간 큽니다. ETL 직후는 수정된 자식만 명시합니다.

핫·콜드 파티션 분리

-- 오래된 파티션을 느린 디스크 tablespace로
CREATE TABLESPACE cold LOCATION '/mnt/hdd/pgdata';
ALTER TABLE events_2024_01 SET TABLESPACE cold;
ALTER INDEX events_2024_01_pkey SET TABLESPACE cold;

운영 패턴: 최근 3개월은 NVMe, 그 이전은 HDD. 자주 안 쓰는 cold 데이터의 디스크 비용 절감합니다.

파티셔닝의 함정

함정영향
파티션 키가 WHERE에 안 들어옴pruning 0% — 단순 분할 손해
unique constraint에 파티션 키 추가 안 함unique 안 됨
글로벌 FK 어려움 (PG 12+ 부분 지원)데이터 무결성 운영 부담
매우 많은 파티션plan time ↑
DEFAULT 파티션이 큼새 파티션 attach 시 검사 오래
파티션 단위 transactional DDL부모 변경이 자식 모두 lock

파티셔닝 도입 결정 재점검

7.1에서 본 결정 기준 + 성능 관점:

상황결정
50GB 이하, 단일 인덱스로 가능파티셔닝 안 함
50~200GB, 보존 정책 명확파티셔닝 검토
200GB+, range 쿼리 위주거의 필수
random 쿼리 위주파티셔닝 효과 작음
매우 많은 small tenanttenant 별 파티션 검토

정리

  • 파티셔닝의 성능 효과 4가지: pruning, wise join, wise aggregate, 인덱스 분할
  • 파티션 키는 쿼리 WHERE에 자주 등장하는 컬럼
  • 활성 파티션 50개 이하가 운영 한계
  • 큰 인덱스 빌드는 자식별 CONCURRENTLY로
  • 핫·콜드 분리로 디스크 비용 절감
  • 도입 전 쿼리 패턴 점검 필수 — pruning 안 되면 손해

다음 절(13.4)에서는 수많은 연결의 부담을 해결하는 커넥션 풀링을 봅니다.