파티셔닝은 논리적으론 하나의 큰 테이블이지만, 물리적으론 여러 개의 작은 조각으로 나눠 저장하는 기법입니다.
크게 두 가지로 나뉩니다.
인덱스 크기 최적화: 테이블이 너무 커지면 인덱스조차 무거워져 검색 속도가 떨어집니다. 파티셔닝을 하면 각 조각마다 인덱스가 따로 생성되므로 매우 가벼워집니다.
파티션 프루닝 (Partition Pruning): 데이터베이스가 쿼리를 분석해 "아, 700,001번 고객은 4번 파티션에만 있겠네!"라고 판단하고 나머지 파티션은 아예 쳐다보지도 않는 기능입니다.
데이터 관리: 1년이 지난 로그 데이터를 지워야 한다면? 거대 테이블에서 DELETE를 실행하는 것보다, 해당 월의 파티션 하나를 통째로 'Drop'하는 것이 수백 배 빠르고 깔끔합니다.
가장 고전적이면서도 강력한 방법입니다. 주로 시간 흐름(Time-series)에 따른 데이터를 다룰 때 필수적입니다.
데이터가 명확한 '범주(Category)'를 가질 때 사용합니다.
데이터를 '골고루' 뿌려주는 것이 목표일 때 사용합니다.
| 구분 | 파티셔닝 (Partitioning) | 샤딩 (Sharding) |
|---|---|---|
| 물리적 위치 | 동일한 데이터베이스 서버 내 | 여러 대의 독립된 서버(호스트) |
| 복잡도 | 상대적으로 낮음 (DB가 관리) | 높음 (애플리케이션에서 분산 로직 필요) |
| 목적 | 단일 서버 내의 성능 및 관리 최적화 | 하드웨어 한계를 넘는 무한 확장성 |
create table grades_org (id serial not null, g int not null);
insert into grades_org(g) select floor(random() * 100) from generate_series (0, 1000000);
create index grades_org_index on grades_org(g);
explain analyze select count(*) from grades_org where g between 30 and 35;
create table grades_parts (id serial not null, g int not null) partition by range(g);
create table g0035 (like grades_parts including indexes);
\d+ g0035
create table g3560 (like grades_parts including indexes);
create table g6080 (like grades_parts including indexes);
create table g80100 (like grades_parts including indexes);
alter table grades_parts attach partition g0035 for values from (0) to (35);
alter table grades_parts attach partition g3560 for values from (35) to (60);
alter table grades_parts attach partition g6080 for values from (60) to (80);
alter table grades_parts attach partition g80100 for values from (80) to (100);
insert into grades_parts select * from grades_org;
select max(g) from g0035; # result max: 34
select max(g) from g3560; # result max: 59
# posgtres 12 이후 부모 테이블에 인덱스를 한 번만 만들면 됨
create index grades_parts_idx on grades_parts(g);
\d g3560 등으로 확인하면 인덱스 생성되어있음
데이터가 RAM에 다 들어가는 규모라면 파티셔닝의 효과는 미미합니다.
만약 데이터가 5억 건이 되어 인덱스가 50GB가 되고, 서버 RAM은 32GB뿐이라면? 이때부터는 메모리에 다 들어가는 5GB짜리 파티션 인덱스와 계속 디스크를 읽어야 하는 50GB짜리 통 인덱스의 속도 차이는 수십, 수백 배로 벌어집니다.
select pg_relation_size(oid), relname from pg_class order by pg_relation_size(oid) desc;
VACUUM이나 REINDEX 같은 관리 작업도 훨씬 빠르고 가볍게 끝납니다.show ENABLE_PARTITION_PRUNING;
set enable_partition_pruning = on;
WHERE g = 30 조건문을 보고 "아, g0035 말고는 답이 없네"라고 판단하여 나머지 3개 파티션을 실행 계획에서 아예 삭제합니다.서비스 운영 중에 수천만 건을 INSERT하면 락(Lock)이 걸려 서비스가 터질 수 있습니다. 대신 별도 테이블에 데이터를 다 담은 뒤, 파티션으로 슥 끼워넣는(Attach) 방식을 쓰면 안전합니다.
행 이동(Row Migration):
까다로운 운영과 스키마 변경:
서비스가 성장하면 테이블 구조를 바꿔야 할 때가 반드시 옵니다.