파티셔닝 — RANGE·LIST·HASH
큰 테이블을 잘게. 시계열·다국가·해시 분산의 표준.
1억 행 테이블에서 "이번 달 데이터만" 을 매번 검색하면 인덱스가 있어도 무겁습니다. 파티셔닝은 테이블을 물리적으로 여러 조각으로 나누어, 쿼리가 필요한 조각만 만지게 합니다. 22편은 PG 의 3가지 파티션 방식과 시계열 데이터 패턴.
왜 파티셔닝? — 4가지 이유
- 쿼리 성능 — 필요한 파티션만 스캔(partition pruning).
- 유지보수 — 옛 파티션 통째로 DROP (1초). DELETE 보다 압도적.
- 인덱스 크기 — 파티션별 인덱스가 작아 캐시 효율 ↑.
- 병렬 처리 — 파티션 단위로 병렬 스캔·VACUUM.
3가지 파티션 방식
| 방식 | 분할 기준 | 주 용도 |
|---|---|---|
| RANGE | 범위 (날짜·숫자 구간) | 시계열·로그 |
| LIST | 값 집합 (국가·지역·카테고리) | 멀티 테넌트·다국가 |
| HASH | 해시 (균등 분산) | 행 분산 (특정 키 핫스팟 없음) |
RANGE — 월별 시계열
-- 1) 파티션 부모 테이블
CREATE TABLE events (
id BIGSERIAL,
user_id BIGINT,
event_type TEXT,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (id, created_at) -- 파티션 키를 PK 에 포함 필수
) PARTITION BY RANGE (created_at);
-- 2) 월별 파티션
CREATE TABLE events_2026_01 PARTITION OF events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE events_2026_02 PARTITION OF events
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
CREATE TABLE events_2026_03 PARTITION OF events
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
-- INSERT 는 부모에 — Postgres 가 자동으로 알맞은 파티션에
INSERT INTO events (...) VALUES (...);
-- SELECT 도 부모에
SELECT * FROM events WHERE created_at >= '2026-02-15';
-- → events_2026_02 와 그 이후만 스캔 (partition pruning)
LIST — 지역별
CREATE TABLE orders_regional (
id BIGSERIAL,
region TEXT NOT NULL,
total NUMERIC,
PRIMARY KEY (id, region)
) PARTITION BY LIST (region);
CREATE TABLE orders_kr PARTITION OF orders_regional FOR VALUES IN ('KR');
CREATE TABLE orders_jp PARTITION OF orders_regional FOR VALUES IN ('JP');
CREATE TABLE orders_us PARTITION OF orders_regional FOR VALUES IN ('US', 'CA');
-- 나머지
CREATE TABLE orders_other PARTITION OF orders_regional DEFAULT;
HASH — 균등 분산
CREATE TABLE users_h (
id BIGINT NOT NULL,
name TEXT,
PRIMARY KEY (id)
) PARTITION BY HASH (id);
-- 4개 파티션 (0, 1, 2, 3)
CREATE TABLE users_h_0 PARTITION OF users_h FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE users_h_1 PARTITION OF users_h FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE users_h_2 PARTITION OF users_h FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE users_h_3 PARTITION OF users_h FOR VALUES WITH (MODULUS 4, REMAINDER 3);
언제 HASH? 시간·값 분포 없이 무조건 균등 분산이 필요할 때. 다만 "특정 user_id 데이터만" 같은 쿼리도 파티션을 하나만 만지게 됨 — id 분포가 골고루면 효과적. 단점: 범위 쿼리는 모든 파티션 스캔.
파티션 인덱스 — 부모에 만들면 모두 상속
-- 부모에 인덱스 → 모든 파티션에 자동
CREATE INDEX ix_events_user ON events (user_id);
-- 또는 파티션별 개별 인덱스
CREATE INDEX ix_events_2026_01_user ON events_2026_01 (user_id);
-- UNIQUE 제약은 파티션 키를 포함해야 함
CREATE UNIQUE INDEX uq_events_id ON events (id, created_at);
partition pruning 확인 — EXPLAIN
EXPLAIN SELECT * FROM events WHERE created_at >= '2026-03-01';
-- 결과
-- Append
-- -> Seq Scan on events_2026_03 ← 이것만!
-- (다른 파티션은 안 나타남 → pruning 성공)
파티션 관리 — 추가·삭제
-- 새 달 파티션 추가
CREATE TABLE events_2026_04 PARTITION OF events
FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
-- 옛 파티션 분리 (백업·아카이브용)
ALTER TABLE events DETACH PARTITION events_2025_01;
-- 분리된 테이블은 일반 테이블 — 백업 후 DROP
-- 또는 즉시 삭제
DROP TABLE events_2025_01; -- 1초 (DELETE 와 비교 불가)
pg_partman — 파티션 자동 관리
CREATE EXTENSION pg_partman;
-- 자동으로 매월 새 파티션 만들고 옛 것 정리
SELECT partman.create_parent(
p_parent_table := 'public.events',
p_control := 'created_at',
p_type := 'range',
p_interval := '1 month',
p_premake := 4 -- 4개월 미리 만들기
);
-- cron 또는 pg_cron 으로 주기 실행
SELECT partman.run_maintenance(); -- 새 파티션 + 옛 것 정리
pg_partman 의 가치. 수동으로 매달 파티션 만들기 → 잊어버리면 사고. pg_partman 이 일정 주기로 미리 만들고 옛 것 보관(detach 또는 drop). 시계열 데이터에는 거의 필수.
실전 — 시계열 데이터 패턴
-- 1) 월별 RANGE 파티션
CREATE TABLE metrics (
ts TIMESTAMPTZ NOT NULL,
device TEXT NOT NULL,
value DOUBLE PRECISION
) PARTITION BY RANGE (ts);
-- 2) BRIN 인덱스 (16편 — 시계열에 최적)
CREATE INDEX ix_metrics_ts ON metrics USING BRIN (ts);
-- 3) pg_partman 자동 관리
-- 4) 90일 보관 후 자동 삭제
SELECT partman.create_parent(
...,
p_premake := 3,
p_retention := '90 days',
p_retention_keep_table := false -- detach 가 아니라 drop
);
제약 — 알아두면 좋은 5가지
1. 파티션 키는 PK·UNIQUE 에 포함이 필수. UNIQUE 검사를 파티션 안에서만 하니까.
2. FK 가 파티션 테이블을 가리키는 것 은 가능하지만, 파티션 테이블에서 다른 테이블로 FK 는 PG 11+ 부터.
3. ALTER TABLE 의 일부 명령은 파티션마다. 부모 → 자식 일괄 적용은 제한적.
4. 너무 작은 파티션(수만 행) 은 오버헤드만 큼. 보통 100만~1억 행 정도 단위.
5. 너무 많은 파티션(수천 개) 은 플래닝 비용 ↑. enable_partition_pruning = on·partition_prune_skip 효과 확인.
23편 — 성능 튜닝 (shared_buffers·work_mem·VACUUM)
설정 파라미터·autovacuum·bloat 잡기.
이전: 21편 복제 · 현재: 22편 (고급) · 다음 → 23편 성능 튜닝 · 진행: 22/24