NoSQL — KV·문서·컬럼·그래프·시계열
관계형 DB는 정형 데이터·강한 일관성·복잡한 조인에 강하지만, (1) 스키마가 유동적이거나, (2) 한 대로 감당 못 할 규모로 수평 확장이 필요하거나, (3) 특정 접근 패턴(그래프 탐색·시계열)에 특화가 필요할 때 다른 도구가 낫다. NoSQL은 "관계형이 아닌" 다양한 데이터 모델의 묶음이다 — 하나의 대안이 아니라 여러 갈래다. 핵심은 *"무엇을 버리고 무엇을 얻는가"*다. (출처: Kleppmann Designing Data-Intensive Applications Ch 2·3 / 각 제품 공식문서.)
왜 NoSQL인가 — ACID를 BASE로 바꾸는 거래
대부분의 NoSQL은 *수평 확장(샤딩)*과 유연한 스키마를 위해 관계형의 무언가를 포기한다 — 보통 조인·다중 행 트랜잭션·강한 일관성 중 일부다. 이 거래를 ACID 대비 BASE로 부른다.
- Basically Available — 일부 노드가 죽어도 응답은 한다.
- Soft state — 외부 입력 없이도 상태가 (복제 수렴으로) 변할 수 있다.
- Eventually consistent — 즉시는 아니어도 언젠가 수렴한다.
또 많은 NoSQL은 집합체 지향(aggregate-oriented) — *함께 읽고 쓰는 데이터를 한 덩어리(문서/레코드)*로 뭉쳐 두어 조인 없이 한 번에 가져온다(대신 중복·갱신 부담).
1) 키-값(Key-Value) — Redis
키로 값을 넣고 뺀다. 가장 단순하고 빠르다(보통 인메모리). Redis는 단순 KV를 넘어 자료구조를 제공해 캐시·세션·랭킹·레이트리밋·큐에 두루 쓰인다.
SET session:abc123 "{...}" EX 1800 # 30분 TTL 세션
INCR page:view:2026-06-04 # 원자적 카운터
EXPIRE cart:42 600 # 10분 후 자동 삭제
# Sorted Set으로 실시간 랭킹 — 점수 기반 정렬을 O(log N)에
ZADD leaderboard 1500 "userA" 1800 "userB"
ZREVRANGE leaderboard 0 9 WITHSCORES # 상위 10명
강점 — 단순 키 접근이 마이크로초. 한계 — 값 내부 조건 검색·조인이 없다(키로만 찾음). 그래서 접근 경로가 키로 분명한 데이터에 쓴다.
2) 문서(Document) — MongoDB
JSON(BSON) 문서를 저장한다. 스키마가 유연하고, *연관을 문서 안에 내포(embed)*해 조인 없이 한 번에 읽는다.
// 주문을 항목까지 한 문서로 내포 — 조인 없이 한 방에 읽음
db.orders.insertOne({
_id: 1, memberId: 42, status: "paid",
items: [ { sku: "A1", qty: 2, price: 9900 },
{ sku: "B7", qty: 1, price: 4900 } ]
});
// 중첩·배열 조건 질의 + 집계 파이프라인
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $unwind: "$items" },
{ $group: { _id: "$memberId", total: { $sum: { $multiply: ["$items.qty","$items.price"] } } } },
{ $sort: { total: -1 } }
]);
*내포(embed) vs 참조(reference)*가 설계의 핵심 — 함께 읽히고 한 문서에 안 너무 커지면 내포, 독립적으로 크게 자라거나 공유되면 참조. 문서 DB도 단일 문서 연산은 원자적이지만 여러 문서 트랜잭션은 (지원하더라도) 비싸다.
3) 와이드 컬럼(Wide-Column) — Cassandra
파티션 키 + 클러스터링 키 + 동적 컬럼 구조로, 초대규모 쓰기·시간순 데이터에 강하고 마스터 없이 수평 분산된다. 핵심 원칙은 쿼리 우선 모델링 — 질의를 먼저 정하고 테이블을 그 질의에 맞춰 만든다(정규화·조인 없음, 중복 감수).
-- 파티션 키(sensor_id) 안에서 시간 역순 정렬 — 시계열 조회에 최적
CREATE TABLE readings (
sensor_id text, ts timestamp, value double,
PRIMARY KEY ((sensor_id), ts)
) WITH CLUSTERING ORDER BY (ts DESC);
SELECT * FROM readings WHERE sensor_id = 's-1' LIMIT 100; -- 한 파티션 = 빠름
주의 — 파티션 키를 안 주는 질의(ALLOW FILTERING)는 전 노드를 훑어 느리고 위험하다. 조인·집계가 필요하면 애플리케이션/별도 테이블로 푼다.
4) 그래프(Graph) — Neo4j
노드·간선으로 관계 자체를 1급으로 다룬다. 친구의 친구·추천·경로처럼 관계형에서 다단계 조인이 폭발하는 질의를 간선을 직접 따라가며 효율적으로 푼다.
// "내 친구의 친구"(2-hop) 추천 — 관계형이라면 self-join 2번
MATCH (me:Person {name:'Kim'})-[:FRIEND]->()-[:FRIEND]->(fof)
WHERE NOT (me)-[:FRIEND]->(fof) AND me <> fof
RETURN DISTINCT fof.name LIMIT 10;
5) 시계열(Time-Series) — InfluxDB·TimescaleDB
시간 인덱스 메트릭에 특화 — 압축·다운샘플링·보존정책(오래된 데이터 자동 만료). IoT·모니터링·금융 틱. PostgreSQL 확장 TimescaleDB처럼 관계형 위 시계열(하이퍼테이블·연속 집계)도 있어, SQL을 그대로 쓰며 시계열 최적화를 얻는다.
NewSQL·멀티모델 — 경계가 흐려진다
요즘은 경계가 섞인다. NewSQL(CockroachDB·Google Spanner·TiDB)은 관계형 SQL + 분산 수평확장 + 강한 일관성을 동시에 노린다(분산 합의로 — [12] 참고). 반대로 PostgreSQL은 *jsonb(문서)·pgvector(벡터)·TimescaleDB(시계열)·hstore(KV)*로 여러 모델을 흡수한다. 그래서 *"NoSQL이냐 RDB냐"*보다 *"이 워크로드에 어떤 모델·일관성·확장 특성이 맞나"*를 묻는 게 옳다.
언제 무엇을 — 선택 기준
| 워크로드 | 적합 | 이유 |
|---|---|---|
| 강한 일관성·복잡 조인·트랜잭션 | 관계형(기본값) | ACID·조인·옵티마이저 |
| 캐시·세션·실시간 랭킹·큐 | KV(Redis) | 키 접근 마이크로초 |
| 유연 스키마·집합체 읽기 | 문서(MongoDB) | 내포로 조인 회피 |
| 초대규모 쓰기·시간순 로그 | 와이드 컬럼(Cassandra) | 쓰기 분산·쿼리 우선 |
| 관계 탐색이 핵심 | 그래프(Neo4j) | 간선 직접 순회 |
| 메트릭·IoT·틱 | 시계열 | 압축·다운샘플·보존 |
흔한 실수 — 유행을 따라 NoSQL부터 고르는 것. 대부분의 서비스는 관계형으로 충분하고, NoSQL은 명확한 이유(규모·접근 패턴·일관성 요구)가 있을 때 도입한다. 그리고 폴리글랏 퍼시스턴스(여러 저장소 혼용)는 운영 복잡도·데이터 동기화라는 비용을 같이 치른다.
한 줄 요약 — NoSQL은 수평 확장·유연 스키마를 위해 조인·트랜잭션·강한 일관성 일부를 포기(BASE·집합체 지향)한 여러 갈래(KV·문서·컬럼·그래프·시계열). 각자 고유한 질의 언어·모델링 원칙이 있다. 관계형을 기본으로 두고, 분명한 이유가 있을 때 골라 섞는다.
(출처: Kleppmann DDIA Ch 2·3 / Redis Data Types·MongoDB Aggregation·Cassandra CQL·Neo4j Cypher·TimescaleDB 공식문서.)