서비스가 커지면 데이터베이스는 반드시 병목 지점이 됩니다. 한 서버의 CPU·메모리·디스크 성능에는 한계가 있기 때문입니다. 이때 등장하는 핵심 기술이 샤딩(Sharding)입니다. 이 글에서는 샤딩의 원리, SQL/NoSQL 사례, 그리고 운영체제·메모리 관점의 내부 동작까지 알아보겠습니다.
샤딩이란?
샤딩은 데이터를 수평 분할(Horizontal Partitioning) 해서 여러 DB 서버(샤드, shard)에 분산 저장하는 기법입니다. 각 샤드는 전체 데이터의 일부만 보유하며, 보통 shared-nothing 아키텍처로 독립적인 리소스(메모리·디스크·WAL)를 가집니다. 이렇게 하면 단일 머신의 한계를 넘어 대규모 데이터와 부하를 처리할 수 있습니다.
- 샤드 키(shard key) 값으로 분할 기준을 정하고, 해시·범위·디렉토리 등으로 샤드를 할당합니다.
- 예를 들어 해시 샤딩에서는 키(예:
userId)를 해시해 나온 값으로 샤드를 선택합니다.
해시 샤딩 원리 그림

1) 샤드 키 컬럼을 선택합니다 → 2) 해시 함수를 적용합니다 → 3) 같은 해시 값은 같은 샤드로 배치됩니다.
샤딩의 목적
- 확장성(Scalability): 노드를 수평으로 추가해 용량과 처리량을 늘립니다.
- 성능 향상(Performance): 각 샤드가 담당하는 데이터가 줄어 조회·수정이 빨라집니다.
- 장애 격리(Fault Isolation): 특정 샤드 장애가 전체 중단으로 번질 가능성을 낮춥니다.
샤드 수평 분산으로 쿼리를 병렬 실행하고, 네트워크 전송을 최소화하도록 설계하면 효과가 커집니다.
샤딩 방식
- 해시 샤딩(Hash Sharding): 키 → 해시 → 균등 분산을 지향합니다.
- 범위 샤딩(Range Sharding): 값의 구간별로 분할합니다(시간·가격·ID 범위 등).
- 디렉토리/존 샤딩(Directory/Zone): 매핑 테이블·정책 기반으로 특정 키/지역을 특정 샤드에 둡니다.
어떤 방식을 택해도 쿼리 라우팅이 핵심이며, 라우터/코디네이터가 샤드 키를 기준으로 대상 샤드를 정합니다.
그렇다면 왜 쿼리 라우팅이 핵심일까요?
샤딩 환경에서는 데이터가 여러 서버(샤드)에 나누어져 저장됩니다. 클라이언트가 “이 데이터 주세요”라고 질의하면, 시스템은 그 데이터가 어느 샤드에 있는지 먼저 찾아야 합니다. 이 과정을 쿼리 라우팅(Query Routing)이라고 합니다.
- SQL (Citus, Vitess 등): 코디네이터 노드가 SQL을 분석해 각 워커(샤드)에 질의를 나눠 보냅니다.
- NoSQL (MongoDB, Cassandra 등): mongos 같은 라우터가 샤드 키와 메타데이터를 보고 적절한 샤드에 질의를 보냅니다.
(1) 성능 차이가 크기 때문
- 샤드 키 기반 라우팅: 쿼리가 바로 특정 샤드 하나로 들어갑니다 → 빠릅니다.
- 샤드 키 없는 쿼리: 모든 샤드를 뒤져야 하는 산발 조회(scatter/gather)가 됩니다 → 느립니다.
즉, 같은 데이터라도 라우팅이 정확히 되면 1샤드만 읽으면 되고, 못 하면 N샤드를 다 뒤져야 합니다.
(2) 확장성을 결정하기 때문
- 샤딩의 목적은 “노드를 추가해 성능을 확장”하는 데 있습니다.
- 하지만 라우팅이 제대로 안 되면 노드 수가 늘어도 모든 쿼리가 모든 샤드를 다 조회해야 해서 확장성이 사라집니다.
(3) 안정성과 일관성에도 영향
- 트랜잭션이나 조인을 같은 샤드 내에서 처리하면 일관성을 유지하기 쉽습니다.
- 라우팅을 잘못하면 여러 샤드를 거치는 복잡한 분산 트랜잭션이 발생해 성능·일관성 문제가 커집니다.
👉 비유: 샤드 없는 DB는 책 한 권, 샤드 있는 DB는 여러 권의 백과사전. 목차(샤드 키)로 바로 권을 찾으면 빠르지만, 목차 없이 모든 책을 다 뒤지면 시간이 오래 걸립니다.
쿼리 라우팅 흐름은 어떻게 될까요?
MongoDB ( mongos 라우팅 흐름 )
Client Query
│
▼
[mongos Router]
│ (샤드 키 확인)
▼
┌───────────────┐
│ ConfigServer │ ← 샤드 키 → 샤드 매핑 메타데이터
└───────────────┘
│
▼
┌─────────┬─────────┬─────────┐
│ Shard A │ Shard B │ Shard C │
└─────────┴─────────┴─────────┘
- 샤드 키 포함: 해당 키 값이 매핑된 샤드만 조회 → 빠른 단일 샤드 쿼리
- 샤드 키 없음: 모든 샤드에 브로드캐스트 후 결과 병합 → 느린 산발 조회
- MongoDB: 라우터(mongos)는 단순히 “쿼리 → 샤드 선택” 역할, 복잡한 집계도 각 샤드가 독립적으로 수행합니다.
PostgreSQL Citus – Coordinator 라우팅 흐름
Client SQL
│
▼
[Coordinator]
│ (Planner/Parser)
▼
┌─────────────────────────┐
│ Query Fragmentation │ → 샤드 키 기반 분할
└─────────────────────────┘
│
▼
┌─────────┬─────────┬─────────┐
│ Worker1 │ Worker2 │ Worker3 │
│ (Shard) │ (Shard) │ (Shard) │
└─────────┴─────────┴─────────┘
│
▼
[Coordinator Merge/Aggregate]
│
▼
Client Response
- 코디네이터는 쿼리를 파싱 후 샤드 키를 기준으로 분할합니다.
- 가능한 연산(WHERE, GROUP BY 등)을 워커에 푸시다운하여 네트워크 전송 최소화합니다.
- 결과를 다시 모아 클라이언트에 반환합니다.
- Citus: 코디네이터가 SQL을 분석·분할하고 최적화 계획을 짠 뒤, 워커에 배포합니다.
✅ 정리하면,
쿼리 라우팅은 샤딩된 데이터베이스에서 질의를 빠르고 효율적으로 처리하기 위한 핵심 메커니즘입니다.
- 라우팅이 잘 되면 → 성능/확장성/안정성이 확보됩니다.
- 라우팅이 잘못되면 → 샤딩의 이점이 사라지고 오히려 병목이 됩니다.
📌 MongoDB (mongos)
- 샤드 키가 포함된 쿼리: 해당 샤드 하나만 조회 → 빠름
- 샤드 키 없는 쿼리: 모든 샤드에 산발 조회 후 병합 → 느림
📌 Citus (PostgreSQL)
- 샤드 키 포함: 코디네이터가 해당 워커만 대상으로 쿼리를 분배 → 효율적
- 샤드 키 없음: 모든 워커에 쿼리 브로드캐스트 + 결과 병합 → 네트워크 비용 증가
그렇다면 실제로 SQL과 NoSQL 시스템에서 샤딩이 어떤 방식으로 구현되는지 구체적으로 살펴보겠습니다.
SQL 시스템 (PostgreSQL + Citus)
Citus는 PostgreSQL을 확장해 코디네이터(coordinator) + 워커(worker) 구조로 샤딩을 지원합니다. 코디네이터는 샤드 메타데이터를 관리하고, 실제 데이터 샤드는 워커 노드에 분산 저장됩니다. 각 워커는 자체 WAL·버퍼 캐시·인덱스를 가진 독립 노드입니다.
코디네이터는 들어온 SQL을 쿼리 프래그먼트로 쪼개 병렬 실행하고, 가능한 연산을 워커에 푸시다운해 네트워크 전송을 최소화합니다.
예제 코드
-- 테이블 생성
CREATE TABLE github_events (
event_id BIGINT,
event_type TEXT,
repo_id BIGINT,
created_at TIMESTAMP
);
-- repo_id 기준 해시 샤딩으로 분산 테이블 설정
SELECT create_distributed_table('github_events', 'repo_id');
-- 데이터 삽입 & 조회
INSERT INTO github_events VALUES (1, 'push', 42, now());
SELECT event_type, COUNT(*)
FROM github_events
WHERE repo_id = 42
GROUP BY event_type;
위 설정은 repo_id 값으로 해시 샤딩하며, 설정된 citus.shard_count만큼 워커에 샤드가 생성됩니다. 이후 질의는 코디네이터가 샤드별로 실행·병합합니다.
- 코로케이션(Colocation): 자주 조인하는 테이블을 같은 샤드 키로 분산해 로컬 조인이 되게 설계합니다.
- 참조 테이블(Reference Table): 작고 공통인 데이터는 모든 워커에 복제해 조회 비용을 줄입니다.
NoSQL 시스템 (MongoDB)
MongoDB 샤딩 클러스터는 샤드(Shard), 구성 서버(Config Server), 라우터(mongos)로 구성됩니다.
- 샤드는 실제 데이터 일부를 저장하며 보통 Replica Set으로 운영됩니다.
- 구성 서버는 샤드 키 배치·메타데이터를 보관합니다.
- mongos는 애플리케이션의 요청을 적절한 샤드로 라우팅합니다.
쿼리에 샤드 키가 포함되면 타깃 샤드만 조회하는 반면, 샤드 키가 없으면 모든 샤드를 산발 조회(scatter/gather) 해야 하므로 비용이 커집니다.
예제 코드
// 데이터베이스 샤딩 활성화
sh.enableSharding("mydb");
// 샤드 키 인덱스 생성
db.users.createIndex({ userId: 1 });
// userId 기준으로 컬렉션 샤딩
sh.shardCollection("mydb.users", { userId: 1 });
mongos가 구성 서버의 메타데이터를 캐시하고, 샤드 키를 기반으로 대상 샤드에만 라우팅합니다.
운영체제 / 메모리 수준 동작
지금까지 SQL(Citus)과 NoSQL(MongoDB)에서 샤딩이 어떻게 구현되는지를 살펴보았습니다. 샤딩은 단순히 데이터베이스 레벨에서 데이터를 나누는 기술이 아니라, 운영체제(OS)와 하드웨어 자원 활용 방식에도 직접적인 영향을 줍니다. 각 샤드는 독립 서버처럼 동작하기 때문에 CPU, 메모리, 디스크, 네트워크 자원을 샤드 단위로 활용하며, 이를 어떻게 설계하느냐에 따라 전체 성능이 크게 달라집니다.
1) 페이지 캐시와 버퍼
- 각 샤드는 별도의 서버·프로세스로 실행되므로, 운영체제의 페이지 캐시도 샤드 단위로 따로 적재됩니다.
- 그 결과, 시스템 전체적으로 보면 총 캐시 용량이 샤드 수만큼 확장되는 효과가 있습니다.
- 하지만 특정 샤드에 핫스팟 데이터가 몰리면 그 샤드의 캐시만 과도하게 사용되어 히트율 저하가 발생할 수 있습니다.
- PostgreSQL(Citus) 워커는 자체 버퍼 캐시 + WAL을 운용하고, MongoDB는 WiredTiger 캐시 + 저널을 각각 관리합니다.
2) 디스크 I/O와 병렬성
- 샤딩은 데이터를 여러 디스크·노드에 흩어 저장하므로, 병렬 디스크 I/O가 가능합니다.
- OS 레벨에서는 멀티큐 I/O 스케줄러가 동작하여 여러 요청을 병렬로 처리합니다.
- 샤드 수가 늘어날수록 I/O 대역폭 자체가 확장되는 효과가 있지만,
- 산발 조회(scatter/gather)
- 대량 샤드 병합 작업에서는 네트워크 트래픽과 머지 비용이 오히려 병목이 됩니다.
3) WAL·OPLOG와 분산 트랜잭션
- Citus(PostgreSQL)
- 각 워커는 로컬 WAL에 커밋합니다.
- 다수 샤드에 걸친 트랜잭션은 2PC (Two-Phase Commit)으로 조율합니다.
- 이 과정에서 prepare → commit 단계마다 네트워크 왕복이 필요하기 때문에, 레이턴시 증가가 불가피합니다.
- MongoDB
- 각 샤드는 oplog를 통해 복제와 트랜잭션을 처리합니다.
- oplog tailing 구조로 동작하기 때문에, replication lag이 커지면 분산 트랜잭션의 일관성이 흔들릴 수 있습니다.
- 샤드 간 트랜잭션은 분산 커밋 프로토콜로 보장하지만, 처리량이 급격히 늘면 성능 부담이 커집니다.
4) 네트워크와 컨텍스트 스위칭
- 샤딩 환경에서는 쿼리가 여러 샤드를 동시에 호출하기 때문에 네트워크 hop 수와 레이턴시가 직접적인 성능 요인이 됩니다.
- 운영체제는 수많은 TCP 커넥션을 관리해야 하므로, 컨텍스트 스위칭 비용이 급증할 수 있습니다.
- 이를 완화하기 위해 보통 커넥션 풀링을 사용하며, 가능한 연산은 샤드 내부에서 처리하도록 쿼리 푸시다운 전략을 씁니다.
5) 메모리/스토리지 불균형
- 샤딩은 이상적으로는 부하를 균등하게 나누지만, 실제 운영에서는 데이터 분포 불균형(핫 샤드 현상)이 발생합니다.
- 특정 샤드의 메모리와 디스크 사용량이 다른 샤드보다 빠르게 차오르면, OS 레벨 스왑·디스크 캐시 미스율 증가로 성능이 떨어집니다.
- 따라서 모니터링 시에는 단순 DB 성능뿐 아니라 샤드별 OS 메트릭 (CPU %, I/O Wait, Cache Hit Ratio 등)도 반드시 확인해야 합니다.
✅ 정리하면
샤딩은 단순히 데이터를 나누는 기술이 아니라,
- 페이지 캐시와 버퍼 효율
- 디스크 I/O 스케줄링
- WAL/OPLOG에 따른 트랜잭션 처리
- 네트워크 연결 관리 같은 운영체제 자원 활용 패턴과 직결됩니다.
따라서 올바른 샤드 키 설계와 쿼리 패턴 최적화뿐 아니라, OS 레벨 성능 지표 모니터링까지 함께 고려해야 안정적인 대규모 시스템을 운영할 수 있습니다.
샤딩의 효과를 제대로 얻으려면 샤드 키 설계가 핵심입니다. 잘못 선택하면 부하 불균형과 핫샤드, 리밸런싱 비용이 커집니다. 특히 해시 샤딩에서는 노드 추가 시 재해시·데이터 이동 비용이 발생할 수 있습니다. 또한 산발 조회(scatter/gather)는 네트워크 왕복과 머지 비용으로 인해 쿼리가 느려질 수 있으므로, 가능하면 샤드 키를 포함한 단일 샤드 질의를 유도하는 것이 중요합니다.
코로케이션과 참조 테이블 복제를 통해 조인·집계를 로컬화하면 성능이 크게 개선됩니다. 운영 중에는 청크 이동·리밸런싱 트래픽이 I/O 병목이 될 수 있으므로 WAL 처리량, 네트워크 지표, 샤드별 p95 레이턴시 같은 메트릭을 모니터링해야 합니다.
결국 샤딩은 단순한 성능 향상 기법이 아니라, 대용량 데이터와 고트래픽 환경에서 유력한 수평 확장 전략입니다. SQL(Citus)과 NoSQL(MongoDB)의 샤딩은 구조는 다르지만, 올바른 샤드 키 설계와 쿼리 라우팅, 푸시다운 최적화라는 공통 원리를 공유합니다. 운영체제 관점에서는 캐시와 I/O를 분산해 총자원을 키우되, 분산 트랜잭션·네트워크 병목이라는 대가가 뒤따릅니다.
따라서 핵심은 샤드 키·쿼리 패턴·데이터 이동 비용을 초기 설계 단계에서 신중히 고려하는 것입니다. 샤딩은 서비스 확장을 위한 전략적 선택이라고 생각합니다.
참고자료
Understanding Database Sharding | DigitalOcean
Understanding Database Sharding | DigitalOcean
www.digitalocean.com
Database Sharding: Concepts & Examples | MongoDB
Database Sharding: Concepts & Examples
Learn what database sharding is, when to use it, and the different types of sharding.
www.mongodb.com
An Overview of Distributed PostgreSQL... | Crunchy Data Blog
An Overview of Distributed PostgreSQL Architectures | Crunchy Data Blog
Marco just joined Crunchy Data and he reflects on his career in distributed systems in this post. He provides an overview of several options for approaching distributed Postgres workloads and the pros and cons of each approach.
www.crunchydata.com
'개발일지 > 데이터베이스' 카테고리의 다른 글
| 데이터베이스 스케일링 전략 (0) | 2024.11.26 |
|---|---|
| 트랜잭션 관리 및 동시성 제어 (0) | 2024.11.25 |
| 쿼리 최적화(Query Optimization) (0) | 2024.11.24 |
| 인덱싱(Indexing) 전략과 최적화 (1) | 2024.11.23 |
| 효과적인 데이터베이스 설계 원칙 (2) | 2024.11.22 |