TL;DR
- NoSQL = "Not Only SQL": 관계형 DB의 대안, 다양한 모델
- 4가지 모델: Document, Key-Value, Wide-column, Graph
- MongoDB: 문서 DB의 표준, JSON 친화적
- Cassandra: 페타바이트 확장, AP 시스템
- DynamoDB: 매니지드, single-digit ms latency
- ScyllaDB: Cassandra 호환, 10배 빠름 (C++)
- 선택 기준: 데이터 모델, 일관성, 운영, 비용
1. NoSQL의 등장
1.1 RDBMS의 한계
1990s-2000s: PostgreSQL, MySQL, Oracle 지배.
문제 (대규모 웹 시대):
- 수평 확장 어려움: 단일 머신 한계
- 고정 스키마: ALTER TABLE 지옥
- JOIN 비용: 분산 환경에서 비효율
- 임피던스 미스매치: OOP ↔ 관계형
- 유연성 부족: 변하는 요구사항
1.2 NoSQL의 부상 (2007~)
Google BigTable (2006), Amazon Dynamo (2007) 논문이 시작.
특징:
- 수평 확장
- 유연한 스키마
- 단순한 CRUD
- 최종 일관성 (대부분)
1.3 NoSQL의 4가지 모델
| 모델 | 예시 | 사용 |
|---|---|---|
| Document | MongoDB, Couchbase | JSON 데이터, 콘텐츠 |
| Key-Value | Redis, DynamoDB, Riak | 캐시, 세션, 단순 조회 |
| Wide-Column | Cassandra, HBase, ScyllaDB | 시계열, IoT, 메시지 |
| Graph | Neo4j, ArangoDB | 관계, 추천, 사기 탐지 |
2. Document DB — MongoDB
2.1 데이터 모델
JSON-like 문서:
{
"_id": "user_123",
"name": "Alice",
"email": "alice@example.com",
"addresses": [
{
"type": "home",
"city": "Seoul",
"zipcode": "12345"
},
{
"type": "work",
"city": "Seoul",
"zipcode": "67890"
}
],
"tags": ["premium", "verified"],
"created_at": "2025-04-15T10:00:00Z"
}
장점:
- 중첩 구조 자연스러움
- 스키마 유연
- 관련 데이터를 한 문서에 (JOIN 회피)
2.2 쿼리
// 삽입
db.users.insertOne({ name: "Alice", email: "alice@example.com" })
// 조회
db.users.findOne({ name: "Alice" })
// 업데이트
db.users.updateOne(
{ _id: "user_123" },
{ $push: { tags: "vip" } }
)
// 집계
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$user_id", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
])
2.3 인덱싱
// Single field
db.users.createIndex({ email: 1 })
// Compound
db.orders.createIndex({ user_id: 1, created_at: -1 })
// Text search
db.articles.createIndex({ content: "text" })
// Geospatial
db.locations.createIndex({ coords: "2dsphere" })
2.4 Replica Set
[Primary]
↓ replication
[Secondary 1] [Secondary 2]
자동 failover: Primary 죽으면 Secondary 승격.
2.5 Sharding
// 샤드 키 설정
sh.shardCollection("mydb.users", { user_id: "hashed" })
자동 분산: MongoDB가 알아서 데이터 이동.
2.6 트랜잭션
MongoDB 4.0+: Multi-document ACID 트랜잭션.
const session = client.startSession()
session.startTransaction()
try {
await accounts.updateOne({ _id: "A" }, { $inc: { balance: -100 } }, { session })
await accounts.updateOne({ _id: "B" }, { $inc: { balance: 100 } }, { session })
await session.commitTransaction()
} catch (error) {
await session.abortTransaction()
}
주의: 분산 트랜잭션은 여전히 비쌈.
2.7 MongoDB 사용 사례
- ✅ 콘텐츠 관리 (블로그, CMS)
- ✅ 카탈로그 (e-commerce)
- ✅ 사용자 프로필
- ✅ 모바일 앱 백엔드 (Realm)
- ❌ 복잡한 JOIN
- ❌ 강한 일관성 필수
- ❌ 페타바이트 분석
3. Wide-Column — Cassandra
3.1 데이터 모델
행마다 다른 컬럼:
user_id | name | email | created_at
---------|-------|-----------------|------------
1 | Alice | a@example.com | 2024-01-01
2 | Bob | (없음) | 2024-01-02
3 | Carol | c@example.com | 2024-01-03
컬럼 패밀리 = 테이블처럼.
3.2 분산 모델
완전 분산:
- Master 없음 (peer-to-peer)
- Consistent hashing
- 각 노드가 일부 데이터
[Token Ring]
Node A (0-25%)
Node B (25-50%)
Node C (50-75%)
Node D (75-100%)
3.3 Replication
keyspace: my_keyspace
replication: { class: 'NetworkTopologyStrategy', dc1: 3 }
3 replicas: 데이터가 3 노드에 복제.
3.4 일관성 수준
Tunable consistency:
SELECT * FROM users WHERE id = 1
USING CONSISTENCY QUORUM;
| 수준 | 설명 |
|---|---|
ONE | 1개 replica |
QUORUM | 과반수 (RF=3이면 2) |
ALL | 모든 replicas |
LOCAL_QUORUM | 같은 DC의 과반수 |
EACH_QUORUM | 각 DC의 과반수 |
R + W > N 공식: 강한 일관성 보장.
- R=1, W=3 (W=ALL): 빠른 읽기, 느린 쓰기
- R=2, W=2 (QUORUM): 균형
- R=3, W=1: 빠른 쓰기, 느린 읽기
3.5 데이터 모델링
RDBMS 사고 X. 쿼리 우선 사고:
-- 잘못 - JOIN 생각
CREATE TABLE users (id, name, email);
CREATE TABLE orders (id, user_id, amount);
-- 사용자의 주문 조회 시 JOIN 필요 → 비효율
-- 올바름 - 비정규화
CREATE TABLE user_orders (
user_id UUID,
order_id UUID,
user_name TEXT,
amount DECIMAL,
PRIMARY KEY (user_id, order_id)
);
-- 한 번의 조회로 OK
원칙: 쓰기는 비싸고 읽기는 싸다 → 데이터 중복 OK.
3.6 Partition Key vs Clustering Key
CREATE TABLE messages (
conversation_id UUID,
message_id TIMEUUID,
user_id UUID,
text TEXT,
PRIMARY KEY (conversation_id, message_id)
);
- Partition key (
conversation_id): 같은 conversation은 같은 노드 - Clustering key (
message_id): 노드 안에서 정렬
효과: 한 conversation의 모든 메시지가 같은 노드에 → 빠른 조회.
3.7 사용 사례
- ✅ 시계열 데이터 (메트릭, 로그)
- ✅ IoT 센서 데이터
- ✅ 메시징 (WhatsApp, Discord)
- ✅ 추천 데이터
- ✅ 페타바이트 데이터
- ❌ 트랜잭션
- ❌ Ad-hoc 쿼리
- ❌ JOIN
3.8 사용 회사
- Apple (수만 노드)
- Netflix
- Uber
- Discord (메시지 → 나중에 ScyllaDB로 이동)
- Spotify
4. AWS DynamoDB
4.1 매니지드 서비스
Amazon이 운영하는 NoSQL.
특징:
- 완전 매니지드
- Single-digit ms latency
- 무한 확장
- Pay per request 또는 Provisioned
- Global Tables (Multi-region active-active)
4.2 데이터 모델
Key-Value + Document:
{
"user_id": "user_123",
"email": "alice@example.com",
"addresses": [
{ "city": "Seoul", "zipcode": "12345" }
]
}
4.3 Primary Key
2가지 방식:
1. Partition Key only (Hash):
user_id (PK)
2. Partition Key + Sort Key (Composite):
user_id (PK) + created_at (SK)
→ 같은 user의 데이터를 시간순 조회.
4.4 Single Table Design
DynamoDB의 핵심 패턴:
전통적 (잘못):
Table Users
Table Orders
Table Products
Single Table (올바름):
Table MyApp:
PK | SK | Type | ...
USER#1 | METADATA | User | name: Alice
USER#1 | ORDER#100 | Order | amount: 50
USER#1 | ORDER#101 | Order | amount: 75
PRODUCT#A | METADATA | Product | price: 10
효과:
- 한 테이블, 한 쿼리로 user + 모든 orders
- JOIN 불필요
단점:
- 학습 곡선 가파름
- 액세스 패턴 미리 알아야
4.5 GSI (Global Secondary Index)
// GSI 생성
{
"IndexName": "EmailIndex",
"KeySchema": [
{ "AttributeName": "email", "KeyType": "HASH" }
],
"Projection": { "ProjectionType": "ALL" }
}
다른 키로 조회 가능.
4.6 가격 모델
Provisioned:
- RCU/WCU 미리 구매
- 안정적 트래픽
On-Demand:
- Pay per request
- 변동 트래픽
예시 비용 (월 1억 read):
- Provisioned: ~$100
- On-Demand: ~$125
4.7 사용 사례
- ✅ 매니지드 필요
- ✅ 예측 가능한 패턴
- ✅ 높은 처리량
- ✅ AWS 생태계
- ❌ 복잡한 쿼리
- ❌ 분석
- ❌ AWS 외 환경
5. ScyllaDB — Cassandra의 진화
5.1 ScyllaDB란?
Cassandra 호환 + C++ 재작성.
비결:
- C++로 모든 것
- shard-per-core 아키텍처
- io_uring
- Seastar framework
효과: Cassandra 대비 5-10배 빠름.
5.2 Cassandra와의 차이
| Cassandra | ScyllaDB | |
|---|---|---|
| 언어 | Java | C++ |
| 아키텍처 | JVM thread pool | Shard-per-core |
| GC | 있음 (JVM) | 없음 |
| 호환성 | - | 100% 호환 |
| 성능 | 기준 | 5-10배 |
| 메모리 | 많음 | 적음 |
5.3 사용 회사
- Discord (Cassandra → ScyllaDB 마이그레이션)
- Comcast
- Disney+
- Samsung
Discord 사례:
- Cassandra: 177 노드
- ScyllaDB: 72 노드
- 비용 60% 절감
6. Couchbase
6.1 특징
MongoDB + Memcached 같은 느낌.
- Document store (JSON)
- Key-value 캐시 내장
- N1QL (SQL-like 쿼리)
- Multi-dimensional scaling
6.2 N1QL
SQL 친화적:
SELECT name, email
FROM users
WHERE city = "Seoul"
ORDER BY created_at DESC
LIMIT 10;
MongoDB의 query language보다 친숙.
6.3 Mobile + Edge
Couchbase Lite: 모바일 임베디드 DB. Sync Gateway: 서버와 동기화.
→ 오프라인 우선 모바일 앱에 강력.
6.4 사용 사례
- ✅ 대형 e-commerce (LinkedIn, eBay)
- ✅ Personalization
- ✅ Offline-first 모바일
- ❌ 작은 프로젝트 (오버킬)
7. 데이터 모델 비교
7.1 같은 데이터, 다른 모델
시나리오: 사용자와 주문.
RDBMS:
CREATE TABLE users (id, name, email);
CREATE TABLE orders (id, user_id, amount);
SELECT * FROM users JOIN orders ON users.id = orders.user_id;
MongoDB (Document):
{
"_id": "user_1",
"name": "Alice",
"orders": [
{ "id": "order_1", "amount": 50 },
{ "id": "order_2", "amount": 75 }
]
}
Cassandra (Wide-column):
CREATE TABLE user_orders (
user_id UUID,
order_id TIMEUUID,
amount DECIMAL,
PRIMARY KEY (user_id, order_id)
);
DynamoDB (Single Table):
PK | SK | data
USER#1 | METADATA | name=Alice
USER#1 | ORDER#1 | amount=50
USER#1 | ORDER#2 | amount=75
Redis (Key-Value):
user:1:name → "Alice"
user:1:orders → [order_1, order_2]
order:1:amount → "50"
7.2 트레이드오프
| RDBMS | Document | Wide-Column | Key-Value | |
|---|---|---|---|---|
| 유연성 | ❌ | ✅ | ✅ | ✅ |
| JOIN | ✅ | 제한적 | ❌ | ❌ |
| 확장성 | 어려움 | 보통 | 매우 우수 | 매우 우수 |
| 트랜잭션 | ✅ | 부분 | 제한적 | 제한적 |
| 쿼리 유연 | ✅ | 좋음 | 제한적 | 단순 키 조회 |
| 학습 곡선 | 보통 | 낮음 | 가파름 | 매우 낮음 |
8. 일관성 모델
8.1 ACID vs BASE
ACID (RDBMS):
- Atomicity
- Consistency
- Isolation
- Durability
BASE (NoSQL):
- Basically Available
- Soft state
- Eventually consistent
8.2 CAP 정리
분산 시스템은 3가지 중 2가지만:
- Consistency
- Availability
- Partition tolerance
P는 필수 (네트워크 분할은 불가피) → CP vs AP.
8.3 NoSQL의 CAP 분류
| CP (일관성) | AP (가용성) | |
|---|---|---|
| MongoDB | ✅ (기본) | 설정 가능 |
| Cassandra | 설정 가능 | ✅ (기본) |
| DynamoDB | 설정 가능 | ✅ |
| HBase | ✅ | |
| Riak | ✅ |
8.4 Tunable Consistency
Cassandra의 강점:
- 쿼리마다 일관성 수준 선택
- ONE, QUORUM, ALL
예시:
- 사용자 프로필 읽기: ONE (빠름)
- 결제 처리: QUORUM (안전)
9. 마이그레이션 사례
9.1 RDBMS → MongoDB
언제:
- JSON 데이터 자연스러움
- 스키마 유연성 필요
- 단일 머신 한계
전략:
- 새 기능을 MongoDB로
- 기존 테이블을 점진적 마이그레이션
- ETL로 데이터 변환
9.2 MongoDB → DynamoDB
Stripe 사례: MongoDB → DynamoDB
- 매니지드 운영 부담 감소
- AWS 통합
도전:
- Single Table Design 학습
- 액세스 패턴 재설계
- 쿼리 유연성 손실
9.3 Cassandra → ScyllaDB
Discord 사례:
- Cassandra 177 노드 → ScyllaDB 72 노드
- 같은 워크로드, 60% 비용 절감
- API 호환 → 코드 변경 거의 없음
9.4 일반적 함정
1. 잘못된 모델 선택:
- "유행하니까 MongoDB"
- "Big data니까 Cassandra"
- → 요구사항에 맞는 선택
2. 데이터 모델링:
- RDBMS 사고로 NoSQL 사용
- 정규화 시도
- → NoSQL은 비정규화
3. 일관성 오해:
- "결국 일관"의 의미
- 트랜잭션 부재
- → 비즈니스 요구 명확히
10. 선택 가이드
10.1 의사결정 트리
강한 일관성 + 트랜잭션이 필수?
├─ 예 → RDBMS (PostgreSQL) 또는 NewSQL (CockroachDB)
└─ 아니오
├─ 데이터가 JSON-like?
│ └─ MongoDB (Document)
├─ 시계열 또는 페타바이트?
│ ├─ 자체 운영 → Cassandra/ScyllaDB
│ └─ 매니지드 → DynamoDB
├─ 그래프 관계?
│ └─ Neo4j
└─ 단순 캐시/세션?
└─ Redis
10.2 사용 사례별
Content Management: MongoDB E-commerce 카탈로그: MongoDB, Couchbase 사용자 프로필: MongoDB, DynamoDB 시계열 (메트릭, 로그): Cassandra, InfluxDB, TimescaleDB 메시지 (채팅): Cassandra, ScyllaDB 세션/캐시: Redis, Memcached 추천 시스템: Cassandra, Neo4j 소셜 그래프: Neo4j, ArangoDB IoT 센서: Cassandra, InfluxDB 금융 거래: PostgreSQL (NoSQL X) 분석: ClickHouse, BigQuery, Snowflake
10.3 운영 고려
Self-managed:
- 전문성 필요
- 비용 통제 가능
- 풀스택 책임
Managed:
- DynamoDB, Atlas (MongoDB), Astra (Cassandra)
- 비용 높음
- 운영 부담 적음
퀴즈
1. NoSQL이 등장한 이유는?
답: RDBMS의 한계: (1) 수평 확장 어려움 — 단일 머신 한계, (2) 고정 스키마 — ALTER TABLE 지옥, (3) JOIN 비용 — 분산 환경에서 비효율, (4) 임피던스 미스매치 — OOP와 관계형 모델의 차이, (5) 유연성 부족. 2007년 Amazon Dynamo 논문이 분기점. Google BigTable, Amazon Dynamo가 영감. NoSQL = "Not Only SQL" — RDBMS를 대체가 아닌 보완.
2. Cassandra의 데이터 모델링 핵심 원칙은?
답: 쿼리 우선 사고입니다. RDBMS처럼 정규화하지 말고, 각 쿼리마다 별도 테이블을 만들 수도 있습니다. 원칙: (1) JOIN 회피 — 데이터 중복 OK, (2) 쓰기 비싸고 읽기 쌈 — 미리 쿼리에 맞게 데이터 구성, (3) Partition key + Clustering key — 같은 partition key는 같은 노드, clustering key로 정렬. 예: (conversation_id, message_id) → 한 conversation의 모든 메시지가 같은 노드에. RDBMS 사고로 사용하면 망함.
3. DynamoDB의 Single Table Design은 무엇인가요?
답: 모든 entity를 한 테이블에 저장하는 패턴. 전통적: Users 테이블, Orders 테이블 분리. Single Table: PK=USER#1, SK=METADATA 또는 SK=ORDER#100. 한 쿼리로 user + 모든 orders 가져오기. JOIN 회피, 비용 감소, 빠른 응답. 단점: 학습 곡선 가파름, 액세스 패턴을 미리 알아야 함, 새 패턴 추가 시 어려움. AWS의 Rick Houlihan이 강력 추천. DynamoDB의 가장 큰 학습 장벽이자 가장 큰 강점.
4. ScyllaDB가 Cassandra보다 빠른 이유는?
답: (1) C++로 재작성 — JVM 오버헤드 없음, (2) GC 없음 — Java의 stop-the-world 회피, (3) Shard-per-core 아키텍처 — 코어마다 독립 shard, 락 없음, (4) io_uring — 진정한 async I/O, (5) Seastar framework — 사용자 공간 네트워킹. 결과: Cassandra 대비 5-10배 빠름. Discord 사례: 177 노드 Cassandra → 72 노드 ScyllaDB, 60% 비용 절감. 100% Cassandra 호환 → 마이그레이션 쉬움.
5. CAP 정리에서 NoSQL DB들의 위치는?
답: 분산 시스템은 C, A, P 중 2가지만 가능. P (네트워크 분할 허용)는 필수 → CP vs AP 선택. CP (일관성 우선): MongoDB (기본), HBase, Spanner — 분할 시 일부 노드 거부. AP (가용성 우선): Cassandra (기본), DynamoDB, Riak — 분할 시 stale data 가능. Tunable: Cassandra는 쿼리마다 일관성 수준 선택 (ONE, QUORUM, ALL). 비즈니스 요구에 따라 결정 — 결제는 CP, 소셜 피드는 AP.
참고 자료
현재 단락 (1/435)
- **NoSQL = "Not Only SQL"**: 관계형 DB의 대안, 다양한 모델