하나의 논리 테이블을 여러 물리 조각(파티션)으로 나눠 저장하는 것. 샤딩과 달리 한 서버 안에서 일어난다.
샤딩과 구분한다
| 파티셔닝 | 한 DB 안에서 테이블을 쪼갠다 | → 관리·조회 효율 |
|---|---|---|
| 샤딩 | 여러 DB 서버로 데이터를 나눈다 | → 쓰기·용량 확장 |
세 가지 방식
-- RANGE — 날짜·숫자 구간 (가장 흔하다)
CREATE TABLE orders (id BIGINT, created_at DATE, amount INT)
PARTITION BY RANGE (created_at);
CREATE TABLE orders_2026_08 PARTITION OF orders
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');
-- LIST — 값 목록
CREATE TABLE member PARTITION BY LIST (country);
CREATE TABLE member_kr PARTITION OF member FOR VALUES IN ('KR');
-- HASH — 고른 분산
CREATE TABLE log PARTITION BY HASH (user_id);
CREATE TABLE log_0 PARTITION OF log FOR VALUES WITH (MODULUS 4, REMAINDER 0);
이득 ① 파티션 프루닝 — 필요한 조각만 읽는다
EXPLAIN SELECT * FROM orders WHERE created_at >= '2026-08-01';
-- Append
-- -> Seq Scan on orders_2026_08 ← 이 파티션만 읽는다
-- (다른 달 파티션은 계획에서 아예 빠진다)
조건에 파티션 키가 없으면 프루닝이 안 된다 — 모든 파티션을 읽게 되어 오히려 느려진다. 파티션 키 선택이 그래서 중요하다.
이득 ② 오래된 데이터를 통째로 버린다
-- DELETE 는 행마다 죽은 튜플을 만들고 VACUUM 부담을 남긴다
DELETE FROM orders WHERE created_at < '2025-01-01'; -- 느리고 bloat 를 만든다
-- 파티션이면 파일을 떼어내면 끝이다
ALTER TABLE orders DETACH PARTITION orders_2024_12;
DROP TABLE orders_2024_12; -- 즉시. bloat 없음
보존 기간이 있는 로그·이력 테이블에서 이것만으로도 파티셔닝을 할 이유가 된다.
주의할 점
- PK·UNIQUE 제약에 파티션 키가 포함돼야 한다 (전역 유니크를 만들 수 없다)
- 파티션이 너무 많으면 계획 수립 자체가 느려진다 (수백 개를 넘기지 않는다)
- 파티션 생성을 자동화해야 한다 — 다음 달 파티션이 없으면 INSERT 가 실패한다
-- 기본 파티션을 두면 어디에도 안 맞는 행을 받아 준다 (INSERT 실패 방지)
CREATE TABLE orders_default PARTITION OF orders DEFAULT;
MySQL도 PARTITION BY RANGE를 지원하지만 PostgreSQL의 선언적 파티셔닝이 프루닝·관리 면에서 더 성숙하다.
면접 함정
- ❌ "파티셔닝하면 무조건 빨라진다" → 파티션 키가 조건에 없으면 모든 파티션을 읽어 더 느려진다.
- ❌ "파티셔닝은 샤딩이다" → 파티셔닝은 한 서버 안이고, 쓰기 확장 효과는 없다.