f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
150 lines
4.2 KiB
Markdown
150 lines
4.2 KiB
Markdown
---
|
|
id: db-sharding-strategies
|
|
title: Sharding — 수평 분할 / 라우팅 / Resharding
|
|
category: Coding
|
|
status: draft
|
|
source_trust_level: B
|
|
verification_status: conceptual
|
|
created_at: 2026-05-09
|
|
updated_at: 2026-05-09
|
|
tags: [database, sharding, scaling, vibe-coding]
|
|
tech_stack: { language: "SQL / Postgres / Citus", applicable_to: ["Backend"] }
|
|
applied_in: []
|
|
aliases: [sharding, partitioning, hash sharding, range sharding, Citus, Vitess]
|
|
---
|
|
|
|
# Sharding
|
|
|
|
> 1대 RDB 한계 (CPU/메모리/디스크) 도달. **여러 노드에 데이터 수평 분할**. Hash / Range / Lookup. **늦게, 진짜 필요할 때만** — read replica + cache + 인덱스 먼저.
|
|
|
|
## 📖 핵심 개념
|
|
- Shard key: 어떤 컬럼으로 분할 (보통 user_id, tenant_id).
|
|
- Hash sharding: hash(key) % N.
|
|
- Range: id 1-1M = shard1, 1M-2M = shard2.
|
|
- Lookup: 어떤 shard 인지 별도 매핑.
|
|
- Resharding: shard 수 변경 시 데이터 이동.
|
|
|
|
## 💻 코드 패턴
|
|
|
|
### App-level sharding (가장 단순)
|
|
```ts
|
|
const SHARDS = [pool0, pool1, pool2, pool3];
|
|
|
|
function shardFor(userId: string): Pool {
|
|
const h = murmurhash(userId);
|
|
return SHARDS[h % SHARDS.length];
|
|
}
|
|
|
|
async function getUser(id: string) {
|
|
const db = shardFor(id);
|
|
return db.users.find(id);
|
|
}
|
|
```
|
|
|
|
### Multi-tenant by row
|
|
```sql
|
|
-- 모든 테이블에 tenant_id
|
|
CREATE TABLE orders (
|
|
id UUID,
|
|
tenant_id UUID NOT NULL,
|
|
...
|
|
PRIMARY KEY (tenant_id, id)
|
|
);
|
|
|
|
CREATE INDEX orders_tenant ON orders(tenant_id);
|
|
|
|
-- 모든 query 에 tenant_id 필수
|
|
SELECT * FROM orders WHERE tenant_id = $1 AND id = $2;
|
|
```
|
|
|
|
RLS (Row Level Security):
|
|
```sql
|
|
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_iso ON orders USING (tenant_id = current_setting('app.tenant_id')::UUID);
|
|
```
|
|
|
|
### Citus (Postgres extension)
|
|
```sql
|
|
SELECT create_distributed_table('orders', 'tenant_id');
|
|
-- Citus 가 hash(tenant_id) 로 자동 분산
|
|
```
|
|
|
|
```sql
|
|
-- 같은 tenant 의 join 은 같은 shard 안 — colocated
|
|
SELECT create_distributed_table('order_items', 'tenant_id', colocate_with => 'orders');
|
|
```
|
|
|
|
### Vitess (MySQL)
|
|
- VTGate 가 query 라우팅.
|
|
- VSchema 로 keyspace + sharding 정의.
|
|
- Vindex 로 lookup 변환.
|
|
- YouTube / Slack / Square 사용.
|
|
|
|
### MongoDB sharding
|
|
```js
|
|
sh.shardCollection('app.orders', { tenantId: 'hashed' });
|
|
```
|
|
|
|
### Resharding (Hash → 더 많은 shard)
|
|
**문제**: hash mod N 변경 시 모든 데이터 이동.
|
|
|
|
**해결**: Consistent hashing — virtual node 로 일부만 이동.
|
|
```ts
|
|
import { HashRing } from 'hashring';
|
|
|
|
const ring = new HashRing(['shard0', 'shard1', 'shard2'], 'md5', { vnodes: 256 });
|
|
const node = ring.get(userId); // 'shard1'
|
|
|
|
// shard 추가
|
|
ring.add('shard3'); // 일부만 재분배
|
|
```
|
|
|
|
### Lookup table (가장 유연)
|
|
```sql
|
|
CREATE TABLE shard_map (
|
|
user_id UUID PRIMARY KEY,
|
|
shard_id INT NOT NULL
|
|
);
|
|
|
|
-- 라우팅: lookup → shard 결정
|
|
-- 조회 비용 1 hop, 단 cache.
|
|
```
|
|
|
|
### Cross-shard query (피하기)
|
|
- 같은 shard key 로 joined 데이터 colocate.
|
|
- Cross-shard 가 필요하면 fan-out + merge.
|
|
- Aggregate 는 분석 DB 로 따로.
|
|
|
|
### Hot tenant 문제
|
|
- 한 tenant 가 너무 큼 → shard 불균등.
|
|
- 해결: 그 tenant 만 별도 shard / sub-shard (user_id 도 같이).
|
|
|
|
## 🤔 의사결정 기준
|
|
| 규모 / 상황 | 추천 |
|
|
|---|---|
|
|
| <1TB / <10K QPS | Sharding 불필요 — 인덱스 / replica / cache |
|
|
| Multi-tenant SaaS | tenant_id sharding (Citus) |
|
|
| Time-series | range sharding (월별 파티션) |
|
|
| 균일 access | hash sharding |
|
|
| Hot key 있음 | + sub-sharding |
|
|
| Geo (지역별) | region 별 cluster |
|
|
| Strong consistency | 같은 shard 안만 |
|
|
|
|
## ❌ 안티패턴
|
|
- **너무 일찍 sharding**: 운영 부담 폭증. 다른 옵션 먼저.
|
|
- **Shard key 변경 가정**: 거의 불가능. 잘 골라야.
|
|
- **Cross-shard transaction 자주**: 2PC 어려움. 디자인 변경.
|
|
- **모든 query 에 shard key 누락**: scatter-gather.
|
|
- **N → N+1 변경 못 함**: consistent hashing 또는 lookup.
|
|
- **Hot tenant 무시**: 한 노드 OOM.
|
|
- **No backup / restore plan**: shard 별 backup 일관성.
|
|
|
|
## 🤖 LLM 활용 힌트
|
|
- 늦게 + 진짜 필요할 때.
|
|
- Citus / Vitess 같은 매니지드 도구 가급적.
|
|
- Tenant_id / user_id 같은 자연 shard key.
|
|
|
|
## 🔗 관련 문서
|
|
- [[DB_Partitioning_Patterns]]
|
|
- [[DB_Read_Replica_Patterns]]
|