이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Partitioning과 sharding은 무엇이 다르고 언제 각각 필요한가?
- Shard key를 고르면 어떤 조회는 빨라지고 어떤 조회는 비싸지는가?
- 데이터 분할은 성능, 정합성, 장애 격리, 운영 복잡도 중 무엇을 바꾸는가?
개요
Partitioning은 큰 데이터를 작은 단위로 나누는 설계다. Sharding은 그 나뉜 데이터를 여러 저장소에 분산하는 설계다. 둘 다 “데이터를 나눈다”는 말로 묶이지만 운영 의미는 다르다. Partitioning은 한 DB 안에서 table이나 index 관리 비용을 낮추는 선택일 수 있다. Sharding은 write, storage, 장애 영향 범위를 여러 DB로 나누는 선택이다. 문제는 분할이 공짜가 아니라는 점이다. 어떤 key로 나누는 순간 그 key와 맞는 query는 쉬워지고, 맞지 않는 query는 어렵거나 느려진다.
이 문서가 바꾸는 설계 축
| 축 | 바뀌는 내용 |
|---|---|
| 성능 | 특정 key 기준 query 범위를 줄이고 write/storage 부하를 분산한다. |
| 정합성 | cross-shard transaction, unique constraint, ordering 보장이 어려워진다. |
| 장애 격리 | shard 단위 장애 격리가 가능하지만 routing 장애가 전체 장애가 될 수 있다. |
| 운영 복잡도 | routing, migration, backup, rebalancing, query 분석 비용이 늘어난다. |
용어부터 분리한다
| 구분 | 의미 | 주로 해결하는 문제 | 새로 생기는 비용 |
|---|---|---|---|
| table partitioning | 한 DB 안에서 table을 범위나 hash로 나눔 | 큰 table 관리, index scan 범위 축소, retention | partition key와 맞지 않는 query 비용 |
| read partition | read model이나 replica를 목적별로 분리 | read latency, report query 격리 | 동기화 지연, stale data |
| horizontal sharding | 같은 schema를 여러 DB에 나눔 | write TPS, storage, 단일 DB 한계 | routing, cross-shard query, rebalancing |
| tenant isolation | tenant별 DB나 schema 분리 | 장애 격리, 규제, noisy neighbor 완화 | 운영 단위 증가, 배포/마이그레이션 증가 |
대표 시나리오
주문 테이블이 2TB까지 커졌고 row는 2억 건이라고 하자. 사용자별 주문 목록 API는 p95 900ms까지 느려졌다. 동시에 관리자 화면은 전체 최신 주문을 시간순으로 조회한다. 주요 query는 다음 두 개다.
select *
from orders
where member_id = ?
order by created_at desc
limit 50;
select *
from orders
order by created_at desc
limit 100;member_id로 shard를 나누면 첫 번째 query는 빠르게 만들 수 있다.
하지만 두 번째 query는 모든 shard에서 최신 주문을 모아 merge해야 한다.
이 선택은 “회원별 UX를 우선한다”는 제품 판단이다.
관리자 최신 주문 화면은 read model, search index, 제한된 시간 범위 같은 별도 설계가 필요하다.
분할 기준 선택
| 후보 key | 좋아지는 경로 | 나빠지는 경로 | 판단 |
|---|---|---|---|
created_at | 기간별 조회, 오래된 데이터 삭제 | 특정 시간대 write 집중 | retention 목적에 좋다. |
member_id | 회원별 주문, 장바구니, 내역 조회 | 글로벌 최신, 판매자별 집계 | 사용자 중심 서비스에 자주 맞다. |
seller_id | 판매자 관리, 정산, 상품 주문 조회 | 구매자별 주문 조회 | marketplace 운영 화면에 유리하다. |
tenant_id | tenant 장애 격리, 계약별 분리 | 대형 tenant hot spot | B2B SaaS에 유리하지만 편차를 본다. |
order_id hash | write 분산 | 사용자별 조회 fanout | write 분산만 보면 유리하다. |
Key 선택은 가장 많은 query를 빠르게 만드는 일이 아니다. 가장 중요한 사용자 경로를 빠르게 만들고, 나머지 경로의 비용을 감당할 수 있게 만드는 일이다.
Partitioning 판단
Partitioning은 sharding보다 운영 변화가 작다. 예를 들어 날짜 기준으로 주문 table을 나누면 오래된 partition 삭제와 최근 데이터 query가 쉬워진다.
create table orders (
id bigint not null,
member_id bigint not null,
created_at timestamp not null,
status varchar(30) not null
) partition by range (created_at);이 구조는 “최근 30일 주문” 같은 query에는 도움이 된다.
하지만 모든 write가 오늘 partition으로 몰리면 write 분산 효과는 작다.
또 member_id 조건만 있고 기간 조건이 없으면 partition pruning을 기대하기 어렵다.
Partitioning은 table 크기 관리에는 좋지만 단일 DB write 한계를 자동으로 없애지는 않는다.
Sharding 판단
Sharding은 단일 DB가 감당하지 못하는 write, storage, 장애 영향 범위를 나눌 때 검토한다. 검토 기준은 보통 다음과 같다.
| 지표 | 신호 | 먼저 확인할 것 |
|---|---|---|
| write TPS | 단일 DB CPU와 lock wait가 계속 포화 | hot row, transaction 길이, batch |
| storage | table/index가 운영 한계를 넘음 | retention, partitioning, 압축 |
| read latency | 특정 key 조회가 느림 | index, cache, replica, query plan |
| 장애 영향 | 한 DB 장애가 모든 tenant를 멈춤 | tenant isolation, read degrade |
| Sharding은 마지막 카드에 가깝다. | ||
| 한 번 들어가면 application, migration, observability, incident response가 모두 바뀐다. |
Routing 코드 경계
Application이 shard를 직접 알기 시작하면 코드 경계가 중요해진다.
public final class ShardRouter {
private final List<DataSource> shards;
public DataSource routeByMemberId(long memberId) {
int index = Math.floorMod(Long.hashCode(memberId), shards.size());
return shards.get(index);
}
}이 코드는 설명용으로는 단순하지만 운영에는 부족하다.
Shard 수가 바뀌면 memberId % shardCount 결과가 바뀌기 때문이다.
실서비스에서는 virtual bucket, routing table, consistent hashing처럼 재배치 비용을 줄이는 장치를 검토한다.
중요한 것은 Controller가 shard를 고르지 않게 하는 것이다.
Service는 business key를 넘기고, routing 책임은 별도 경계에 둔다.
Cross-shard query 비용
Shard key와 맞지 않는 query는 비싸진다. 예를 들어 관리자 최신 주문은 모든 shard를 조회해야 한다.
query shard_0 latest 100
query shard_1 latest 100
query shard_2 latest 100
query shard_3 latest 100
merge and sort top 100Shard가 4개일 때는 가능해 보인다. Shard가 64개가 되면 timeout, partial failure, pagination 정합성이 문제가 된다. 대안은 다음 중 하나다.
| 대안 | 장점 | 비용 |
|---|---|---|
| read model | global query가 빨라진다. | 동기화 지연과 재처리 필요 |
| search index | 검색과 정렬이 유연하다. | 색인 지연과 재색인 비용 |
| query 제한 | 운영 부담이 낮다. | 관리자 기능이 제한된다. |
| shard fanout | 별도 저장소가 줄어든다. | timeout과 partial result 처리 필요 |
Transaction과 ID
Sharding은 transaction 경계를 바꾼다. 한 shard 안의 주문 생성은 기존 DB transaction으로 처리할 수 있다. 하지만 다른 shard의 쿠폰, 포인트, 정산 데이터를 함께 바꾸면 분산 transaction 문제가 된다. 가능한 설계는 세 가지다.
| 선택 | 정합성 | 비용 |
|---|---|---|
| 같은 shard에 함께 배치 | 강한 정합성을 유지하기 쉽다. | key 선택이 제한된다. |
| Saga/outbox | 단계별 보상과 재처리 가능 | eventual consistency를 받아들인다. |
| 중앙 owner 유지 | 중요한 제약을 한 곳에서 보장 | 중앙 병목이 남는다. |
| ID도 마찬가지다. | ||
| Auto increment는 shard별로 충돌할 수 있다. | ||
| Snowflake류 ID, shard prefix, 별도 ID service 같은 선택지가 필요하다. |
Migration 순서
Sharding은 설계 문서보다 migration이 더 어렵다. 현실적인 순서는 보통 다음과 같다.
- 현재 query와 key 분포를 측정한다.
- shard key 후보별로 query 영향도를 계산한다.
- routing layer를 먼저 만들고 아직 단일 DB로 보낸다.
- 일부 tenant나 bucket만 새 shard로 옮긴다.
- dual read나 검증 query로 row count와 checksum을 확인한다.
- rollback 기준과 freeze window를 정한다. 이 순서가 없으면 sharding은 성능 개선 작업이 아니라 데이터 이전 사고가 된다.
개인 프로젝트 기준
member_id,tenant_id,created_at중 어떤 key가 어떤 API를 빠르게 하는지 표로 남긴다.- 실제 sharding을 구현하지 않더라도 routing layer와 cross-shard query 비용을 설명한다.
- shard key와 맞지 않는 관리자 query를 어떻게 처리할지 적는다.
- auto increment ID가 왜 문제가 되는지 설명한다.
- 포트폴리오에서는 “샤딩 적용”보다 “왜 아직 적용하지 않았는가”도 좋은 답변이 된다.
기업 운영 기준
- shard key 후보는 실제 traffic trace와 query log로 검증한다.
- shard별 QPS, p95 latency, CPU, lock wait, storage growth를 dashboard로 분리한다.
- routing table 변경은 versioning, rollout, rollback 절차를 가진다.
- backup, restore, schema migration은 shard 단위와 전체 단위 모두 훈련한다.
- cross-shard query는 timeout, partial failure, pagination 기준을 문서화한다.
위험 신호
- table이 크다는 이유만으로 sharding을 먼저 선택한다.
- shard key를 정했지만 가장 중요한 API query와 연결하지 못한다.
- global unique constraint가 필요한데 shard별 DB만 생각한다.
- 관리자 query를 모든 shard fanout으로 처리하면서 timeout 정책이 없다.
- shard 수 변경 때 데이터가 얼마나 이동하는지 계산하지 않는다.
- partitioning과 sharding을 같은 운영 비용으로 취급한다.
확인 질문
확인 질문
- 확인 질문: Partitioning과 sharding의 가장 큰 차이는 무엇인가?
- 답변: partitioning은 주로 한 저장소 안의 데이터 관리 단위이고, sharding은 여러 저장소로 write/storage/장애 범위를 나누는 설계다.
- 확인 질문: 좋은 shard key란 무엇인가?
- 답변: 핵심 사용자 경로를 빠르게 만들고, 반대쪽 query와 rebalancing 비용을 감당 가능한 수준으로 만드는 key다.
- 확인 질문: Sharding이 정합성에 주는 영향은 무엇인가?
- 답변: cross-shard transaction, global unique constraint, ordering, pagination을 어렵게 만들어 별도 owner나 eventual consistency 설계가 필요해진다.