샤딩과 파티셔닝, 무엇이 다르고 언제 써야 할까?
대용량 데이터를 다루다 보면 언젠가 이런 질문을 만나게 된다.
“테이블이 너무 커졌는데 파티셔닝을 해야 할까?”
“DB 부하가 높은데 샤딩을 해야 할까?”
“둘 다 데이터를 나누는 것 같은데 차이가 뭘까?”
샤딩과 파티셔닝은 모두 데이터를 나눈다는 점에서는 비슷하다.
하지만 두 기술이 해결하려는 문제는 다르다.
간단히 말하면 다음과 같다.
| 구분 | 핵심 목적 |
|---|---|
| 파티셔닝 | 하나의 DB 안에서 큰 테이블을 관리하기 쉽게 나누는 것 |
| 샤딩 | 여러 DB 서버로 데이터를 분산해 저장·처리하는 것 |
즉, 파티셔닝은 테이블 관리와 쿼리 최적화에 가깝고, 샤딩은 시스템 확장성에 가깝다.
1. 파티셔닝이란?
파티셔닝은 하나의 논리적인 테이블을 여러 물리적인 단위로 나누는 방식이다.
예를 들어 drone_position_log 테이블에 하루 수백만 건의 위치 데이터가 쌓인다고 해보자.
CREATE TABLE drone_position_log (
id BIGINT NOT NULL,
drone_id VARCHAR(50) NOT NULL,
latitude DOUBLE PRECISION NOT NULL,
longitude DOUBLE PRECISION NOT NULL,
altitude DOUBLE PRECISION NOT NULL,
received_at TIMESTAMP NOT NULL
);
이 테이블이 1년, 2년 계속 쌓이면 조회, 삭제, 인덱스 관리 비용이 커진다.
이때 received_at 기준으로 월별 파티션을 나눌 수 있다.
CREATE TABLE drone_position_log (
id BIGINT NOT NULL,
drone_id VARCHAR(50) NOT NULL,
latitude DOUBLE PRECISION NOT NULL,
longitude DOUBLE PRECISION NOT NULL,
altitude DOUBLE PRECISION NOT NULL,
received_at TIMESTAMP NOT NULL
) PARTITION BY RANGE (received_at);
CREATE TABLE drone_position_log_2026_07
PARTITION OF drone_position_log
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
CREATE TABLE drone_position_log_2026_08
PARTITION OF drone_position_log
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');
애플리케이션은 여전히 drone_position_log 테이블을 조회하지만, DB 내부에서는 조건에 맞는 파티션만 접근할 수 있다.
PostgreSQL은 선언적 파티셔닝을 제공하며, 파티션 테이블은 부모 테이블과 동일한 컬럼 구조를 가진다.
MySQL 역시 파티셔닝을 통해 하나의 테이블 데이터를 여러 파티션으로 분리할 수 있고, 오래된 데이터는 해당 파티션을 제거하는 방식으로 관리할 수 있다.
2. 파티셔닝의 장점
파티셔닝의 가장 큰 장점은 큰 테이블을 작은 단위로 관리할 수 있다는 것이다.
2-1. 조회 범위를 줄일 수 있다
예를 들어 최근 1일치 위치 데이터만 조회하는 쿼리가 있다고 하자.
SELECT *
FROM drone_position_log
WHERE received_at >= '2026-07-07 00:00:00'
AND received_at < '2026-07-08 00:00:00';
received_at 기준으로 파티셔닝되어 있다면 DB는 전체 테이블을 훑지 않고 해당 날짜가 포함된 파티션만 접근할 수 있다.
이를 보통 partition pruning이라고 한다.
다만 여기서 중요한 점이 있다.
파티셔닝을 했다고 모든 쿼리가 빨라지는 것은 아니다.
쿼리 조건에 파티션 키가 포함되지 않으면 여러 파티션을 모두 확인해야 할 수 있다.
좋은 예:
SELECT *
FROM drone_position_log
WHERE received_at >= '2026-07-01'
AND received_at < '2026-08-01';
좋지 않은 예:
SELECT *
FROM drone_position_log
WHERE drone_id = 'DRONE-001';
위 쿼리는 received_at 조건이 없기 때문에 월별 파티션을 모두 탐색해야 할 수 있다.
따라서 파티셔닝 키는 실제 조회 패턴을 기준으로 정해야 한다.
2-2. 오래된 데이터 삭제가 쉬워진다
대용량 로그 테이블에서 오래된 데이터를 삭제할 때 다음과 같은 쿼리를 자주 사용한다.
DELETE FROM drone_position_log
WHERE received_at < '2025-01-01';
문제는 이런 대량 DELETE가 DB에 큰 부담을 준다는 점이다.
- 많은 row delete 발생
- undo/redo log 증가
- lock 경합 가능성
- vacuum 또는 purge 비용 증가
- 인덱스 정리 비용 증가
하지만 월별 파티셔닝이 되어 있다면 오래된 파티션 자체를 제거할 수 있다.
DROP TABLE drone_position_log_2025_01;
또는 DBMS에 따라 파티션 DROP 문법을 사용할 수 있다.
이 방식은 row 단위 삭제보다 훨씬 관리가 쉽다.
특히 로그성 데이터, 이력 데이터, 이벤트 데이터처럼 보관 주기가 명확한 테이블에서는 파티셔닝의 효과가 크다.
2-3. 인덱스 관리 범위를 줄일 수 있다
대용량 테이블에서 인덱스 크기가 커지면 조회뿐 아니라 INSERT/UPDATE 성능에도 영향을 준다.
파티셔닝을 하면 파티션별로 인덱스를 관리할 수 있기 때문에 특정 기간 데이터에 대한 인덱스 관리 비용을 줄일 수 있다.
예를 들어 최근 3개월 데이터는 자주 조회되고, 1년 전 데이터는 거의 조회되지 않는다면 최근 파티션 중심으로 인덱스 전략을 다르게 가져갈 수도 있다.
3. 파티셔닝의 한계
파티셔닝은 강력하지만 만능은 아니다.
가장 중요한 한계는 하나의 DB 서버 안에서 동작한다는 점이다.
물론 파티션이 여러 파일이나 테이블스페이스로 나뉠 수는 있지만, 기본적으로 하나의 DB 인스턴스가 쿼리 처리, 커넥션 처리, 트랜잭션 처리를 담당한다.
따라서 다음과 같은 문제는 파티셔닝만으로 해결하기 어렵다.
| 문제 | 파티셔닝으로 해결 가능? |
|---|---|
| 테이블 관리 비용 증가 | 가능 |
| 오래된 데이터 삭제 비용 | 가능 |
| 특정 기간 조회 최적화 | 가능 |
| 단일 DB CPU 한계 | 어려움 |
| 단일 DB 커넥션 한계 | 어려움 |
| 단일 DB 쓰기 처리량 한계 | 어려움 |
| 스토리지 용량 한계 | 부분적 |
| 장애 격리 | 어려움 |
즉, 파티셔닝은 큰 테이블을 잘게 나누는 기술이지, DB 서버 자체를 여러 대로 확장하는 기술은 아니다.
4. 샤딩이란?
샤딩은 데이터를 여러 DB 서버에 나누어 저장하는 방식이다.
예를 들어 사용자 데이터를 user_id 기준으로 4개의 DB에 나누어 저장한다고 해보자.
user_id % 4 = 0 -> shard_0
user_id % 4 = 1 -> shard_1
user_id % 4 = 2 -> shard_2
user_id % 4 = 3 -> shard_3
각 샤드는 독립적인 DB 서버다.
Application
|
Shard Routing Layer
┌───────────┼───────────┐
| | |
shard_0 shard_1 shard_2 shard_3
이 구조에서는 데이터 저장과 조회 부하가 여러 DB 서버로 분산된다.
즉, 샤딩의 목적은 수평 확장이다.
MongoDB도 샤딩에서 shard key를 사용해 문서를 여러 샤드에 분산하며, shard key는 단일 필드 또는 복합 인덱스 기반 필드가 될 수 있다.
MongoDB 공식 문서에서도 좋은 shard key는 데이터를 균등하게 분산하면서 일반적인 쿼리 패턴을 지원해야 한다고 설명한다.
5. 샤딩의 장점
샤딩의 핵심 장점은 단일 DB 서버의 한계를 넘을 수 있다는 것이다.
5-1. 쓰기 처리량을 분산할 수 있다
단일 DB에 모든 INSERT가 몰리면 CPU, WAL/redo log, 디스크 I/O, lock, connection pool 등이 병목이 된다.
샤딩을 하면 쓰기 요청이 여러 DB로 분산된다.
DRONE-001 -> shard_1
DRONE-002 -> shard_2
DRONE-003 -> shard_3
DRONE-004 -> shard_0
위치 데이터처럼 지속적으로 INSERT가 발생하는 시스템에서는 샤딩이 쓰기 처리량 확장에 도움이 될 수 있다.
5-2. 저장 용량을 분산할 수 있다
하나의 DB 서버가 감당할 수 있는 스토리지에는 한계가 있다.
샤딩을 사용하면 데이터를 여러 서버에 나눠 저장할 수 있다.
shard_0: 2TB
shard_1: 2TB
shard_2: 2TB
shard_3: 2TB
단일 서버 2TB 한계를 넘어서 전체 8TB 규모로 데이터를 확장할 수 있다.
5-3. 장애 영향을 줄일 수 있다
샤딩 구조에서는 특정 샤드 장애가 전체 데이터 장애로 이어지지 않을 수 있다.
물론 샤드별 복제, failover, 라우팅 정책이 제대로 구성되어 있어야 한다.
예를 들어 shard_2에 장애가 발생하면 shard_2에 속한 사용자나 도메인의 요청은 실패할 수 있지만, 다른 샤드의 요청은 계속 처리될 수 있다.
6. 샤딩의 단점
샤딩은 확장성 측면에서는 강력하지만, 운영 난이도가 크게 올라간다.
6-1. 샤드 키 선택이 어렵다
샤딩에서 가장 중요한 것은 샤드 키다.
샤드 키를 잘못 잡으면 특정 샤드에 데이터나 트래픽이 몰린다.
예를 들어 created_at을 기준으로 샤딩한다고 해보자.
2026-07-07 데이터 -> shard_3
최신 데이터가 항상 특정 샤드로 몰릴 수 있다.
이 경우 샤딩을 했지만 실제로는 하나의 샤드만 바쁜 구조가 된다.
이를 hot shard 문제라고 볼 수 있다.
좋은 샤드 키는 보통 다음 조건을 만족해야 한다.
| 조건 | 설명 |
|---|---|
| 높은 분산도 | 값이 다양해야 한다 |
| 균등한 분포 | 특정 값에 데이터가 몰리지 않아야 한다 |
| 주요 조회 조건 포함 | 자주 조회하는 조건과 맞아야 한다 |
| 변경 가능성 낮음 | 샤드 키 값이 자주 바뀌면 안 된다 |
| 재샤딩 비용 고려 | 향후 샤드 확장 시 이동 비용이 감당 가능해야 한다 |
예를 들어 사용자 기반 서비스라면 user_id, 테넌트 기반 SaaS라면 tenant_id, 드론 위치 시스템이라면 drone_id 또는 operation_id가 후보가 될 수 있다.
6-2. 크로스 샤드 쿼리가 어려워진다
샤딩된 시스템에서 다음 쿼리를 생각해보자.
SELECT *
FROM drone_position_log
WHERE received_at >= '2026-07-07 00:00:00'
AND received_at < '2026-07-08 00:00:00';
만약 데이터가 drone_id 기준으로 샤딩되어 있다면, 특정 날짜 데이터를 조회하기 위해 모든 샤드를 조회해야 한다.
shard_0 조회
shard_1 조회
shard_2 조회
shard_3 조회
결과 merge
이런 쿼리를 fan-out query라고 볼 수 있다.
샤딩 환경에서는 단일 DB에서 쉽게 하던 JOIN, GROUP BY, ORDER BY, pagination도 어려워진다.
6-3. 트랜잭션이 복잡해진다
단일 DB에서는 하나의 트랜잭션으로 처리할 수 있던 작업도 샤딩 환경에서는 여러 DB에 걸칠 수 있다.
예를 들어 계좌 이체를 생각해보자.
A 사용자 계좌 -> shard_1
B 사용자 계좌 -> shard_3
A에서 돈을 빼고 B에게 돈을 넣는 작업이 서로 다른 샤드에서 발생한다.
이 경우 분산 트랜잭션, Saga 패턴, 보상 트랜잭션, Outbox 패턴 등을 고려해야 한다.
샤딩은 단순히 DB를 나누는 문제가 아니라 애플리케이션의 트랜잭션 모델을 바꾸는 문제다.
7. 파티셔닝과 샤딩 비교
두 기술을 비교하면 다음과 같다.
| 항목 | 파티셔닝 | 샤딩 |
|---|---|---|
| 데이터 분리 단위 | 하나의 DB 내부 테이블 분리 | 여러 DB 서버로 분리 |
| 주 목적 | 대용량 테이블 관리, 조회 최적화 | 수평 확장, 부하 분산 |
| 애플리케이션 영향 | 상대적으로 작음 | 큼 |
| 쿼리 복잡도 | 비교적 낮음 | 높음 |
| 트랜잭션 복잡도 | 기존 DB 트랜잭션 유지 가능 | 분산 트랜잭션 문제 발생 가능 |
| 운영 난이도 | 중간 | 높음 |
| 장애 격리 | 제한적 | 샤드 단위 격리 가능 |
| 대표 기준 | 날짜, 범위, 상태, 지역 | user_id, tenant_id, account_id, domain_id |
| 도입 난이도 | 비교적 낮음 | 높음 |
실무적으로는 다음 순서로 판단하는 것이 좋다.
1. 인덱스와 쿼리 튜닝으로 해결 가능한가?
2. 테이블이 너무 커져서 관리가 어려운가?
3. 데이터 보관 주기가 명확한가?
4. 특정 기간/범위 조회가 대부분인가?
-> 파티셔닝 우선 검토
5. 단일 DB 서버의 CPU, I/O, 커넥션, 쓰기 처리량 한계인가?
6. 데이터 용량이 단일 서버 한계를 넘는가?
7. 트래픽을 도메인 키 기준으로 분산할 수 있는가?
-> 샤딩 검토
8. 실무 예시: 드론 위치 데이터 저장 시스템
드론 위치 데이터는 다음 특성을 가진다.
- 지속적인 INSERT 발생
- 시간 기준 조회가 많음
- 최근 데이터 조회 비중이 높음
- 오래된 데이터는 보관 또는 삭제 대상
- 특정 드론의 이동 경로 조회 필요
- 데이터가 누적되면 테이블 크기가 빠르게 증가
이 경우 처음부터 샤딩을 도입하기보다는 보통 다음 단계가 더 현실적이다.
1단계: 인덱스 최적화
CREATE INDEX idx_drone_position_received_at
ON drone_position_log (received_at);
CREATE INDEX idx_drone_position_drone_time
ON drone_position_log (drone_id, received_at);
최근 데이터 조회와 특정 드론의 시간대별 조회를 우선 최적화한다.
2단계: 시간 기준 파티셔닝
drone_position_log_2026_07
drone_position_log_2026_08
drone_position_log_2026_09
위치 데이터는 대부분 시간 기준으로 보관·삭제·조회가 이루어지기 때문에 received_at 기준 파티셔닝이 잘 맞는다.
SELECT *
FROM drone_position_log
WHERE drone_id = 'DRONE-001'
AND received_at >= '2026-07-07 10:00:00'
AND received_at < '2026-07-07 11:00:00';
이 쿼리는 파티션 키인 received_at과 보조 조건인 drone_id를 함께 사용하므로 효율적으로 동작할 가능성이 높다.
3단계: 단일 DB 한계 도달 시 샤딩 검토
만약 하루 수천만 건 이상 위치 데이터가 쌓이고, 단일 DB의 쓰기 처리량이 한계에 도달한다면 샤딩을 검토할 수 있다.
예를 들어 operation_id 기준으로 샤딩할 수 있다.
operation_id % 4 = 0 -> shard_0
operation_id % 4 = 1 -> shard_1
operation_id % 4 = 2 -> shard_2
operation_id % 4 = 3 -> shard_3
또는 drone_id 기준으로 샤딩할 수도 있다.
hash(drone_id) % shard_count
하지만 이 경우 조회 패턴을 반드시 확인해야 한다.
| 조회 패턴 | 적합한 샤드 키 후보 |
|---|---|
| 특정 드론의 이동 경로 조회 | drone_id |
| 특정 작전/임무 단위 조회 | operation_id |
| 특정 기관/테넌트별 데이터 격리 | tenant_id |
| 전체 시간대 통계 조회 | 샤딩 부적합 가능성 있음 |
전체 시간대 통계가 많다면 샤딩 후 모든 샤드를 조회해야 할 수 있다.
이런 경우에는 OLTP DB에서 직접 통계 쿼리를 하기보다 Kafka, RabbitMQ, Redis Stream, ClickHouse, Elasticsearch, Data Warehouse 등 별도 분석 저장소를 함께 고려하는 것이 더 적절할 수 있다.
9. 파티셔닝 키와 샤드 키는 다르게 잡을 수 있다
중요한 점은 샤딩 키와 파티셔닝 키가 반드시 같을 필요는 없다는 것이다.
예를 들어 다음과 같은 구조가 가능하다.
Shard key : operation_id
Partition key : received_at
구조는 다음과 같다.
shard_0
├── drone_position_log_2026_07
├── drone_position_log_2026_08
shard_1
├── drone_position_log_2026_07
├── drone_position_log_2026_08
shard_2
├── drone_position_log_2026_07
├── drone_position_log_2026_08
이렇게 하면 다음 효과를 얻을 수 있다.
operation_id기준으로 DB 부하 분산received_at기준으로 각 샤드 내부 테이블 관리- 오래된 데이터는 샤드별 파티션 단위로 제거
- 특정 작전 단위 데이터는 특정 샤드에서 조회
샤딩과 파티셔닝은 경쟁 관계가 아니라 조합 가능한 기술이다.
다만 처음부터 이 구조를 도입하면 복잡도가 급격히 올라가므로, 실제 병목이 확인된 뒤 단계적으로 접근하는 것이 좋다.
10. 실무 의사결정 기준
샤딩과 파티셔닝을 결정할 때는 “데이터가 많다”라는 이유만으로 판단하면 안 된다.
더 정확한 질문은 다음과 같다.
파티셔닝이 적합한 경우
| 상황 | 설명 |
|---|---|
| 시간 기준 데이터가 계속 쌓인다 | 로그, 이력, 이벤트, 위치 데이터 |
| 오래된 데이터 삭제 정책이 명확하다 | 3개월, 6개월, 1년 보관 |
| 특정 기간 조회가 많다 | 최근 1시간, 최근 1일, 특정 월 |
| 테이블 크기로 인덱스 관리가 부담된다 | 인덱스 rebuild, vacuum, purge 비용 증가 |
| 단일 DB 서버 성능은 아직 감당 가능하다 | CPU/I/O 한계는 아님 |
샤딩이 적합한 경우
| 상황 | 설명 |
|---|---|
| 단일 DB의 쓰기 처리량이 한계다 | INSERT/UPDATE가 병목 |
| 단일 DB의 저장 용량 한계에 도달했다 | 스토리지 확장 한계 |
| 트래픽을 특정 키 기준으로 나눌 수 있다 | user_id, tenant_id, operation_id |
| 장애 격리를 샤드 단위로 하고 싶다 | 일부 도메인 장애 격리 |
| 애플리케이션에서 라우팅 복잡도를 감당할 수 있다 | shard routing layer 필요 |
11. 도입 전 반드시 확인해야 할 지표
샤딩이나 파티셔닝을 도입하기 전에 다음 지표를 먼저 확인해야 한다.
| 지표 | 확인 이유 |
|---|---|
| 테이블 row 수 | 실제로 테이블 규모가 문제인지 확인 |
| 테이블/인덱스 크기 | 데이터보다 인덱스가 병목일 수 있음 |
| 주요 쿼리 패턴 | 파티션 키/샤드 키 결정 기준 |
| slow query | 실제 병목 쿼리 확인 |
| TPS/QPS | 읽기/쓰기 부하 비율 확인 |
| DB CPU 사용률 | 단일 DB 연산 한계 확인 |
| Disk I/O | INSERT, index update 병목 확인 |
| Connection 사용률 | DB connection pool 한계 확인 |
| Lock wait | 쓰기 경합 확인 |
| 데이터 보관 정책 | 파티션 drop 전략 수립 |
특히 샤딩은 되돌리기 어렵다.
따라서 “언젠가 커질 것 같아서”가 아니라, 현재 병목이 명확하고 샤딩으로 해결되는 병목인지를 확인해야 한다.
12. 결론
샤딩과 파티셔닝은 둘 다 데이터를 나누는 기술이지만, 목적이 다르다.
파티셔닝은 하나의 DB 안에서 큰 테이블을 관리하기 쉽게 나누는 기술이다.
시간 기준 로그, 이벤트, 위치 데이터처럼 보관 주기와 조회 범위가 명확한 데이터에 적합하다.
샤딩은 여러 DB 서버로 데이터를 분산해 단일 DB의 처리량과 용량 한계를 넘기 위한 기술이다.
하지만 샤드 키 선택, 크로스 샤드 쿼리, 분산 트랜잭션, 운영 자동화 등 복잡도가 크게 증가한다.
실무적으로는 다음 순서가 가장 안전하다.
인덱스/쿼리 튜닝
→ 파티셔닝
→ 읽기 복제/캐시/비동기 처리
→ 그래도 단일 DB 한계라면 샤딩
샤딩은 강력한 확장 수단이지만, 가장 먼저 꺼내야 할 카드는 아니다.
대부분의 시스템에서는 먼저 파티셔닝과 인덱스 전략만으로도 상당한 성능 개선과 운영 안정성을 얻을 수 있다.
중요한 것은 기술 이름이 아니라 병목의 위치다.
- 테이블 관리와 기간 조회가 문제라면 파티셔닝
- 단일 DB 서버의 처리량과 용량이 문제라면 샤딩
- 둘 다 문제라면 샤딩 내부에서 파티셔닝을 함께 사용
이 기준을 잡고 접근하면 샤딩과 파티셔닝을 단순한 개념이 아니라 실무적인 설계 선택지로 판단할 수 있다.
'나의 주니어 개발 일기 > DB' 카테고리의 다른 글
| Supabase 도커(Docker)로 로컬 개발 환경 구축하기 (0) | 2026.02.02 |
|---|---|
| DB 이중화와 MySQL Replication 구조 정리 및 실무 도입 방향성 (0) | 2025.09.05 |
| 커버링 인덱스, 클러스터드 인덱스, 넌클러스터드 인덱스 (1) | 2024.07.26 |
| DB Replication 해보기 (master-slave) by 도커 컴포즈 (2) | 2024.05.17 |
| 클러스터드 인덱스 vs 넌클러스터드 인덱스 (0) | 2024.05.08 |
