- Published on
데이터베이스 완전 비교 2025: PostgreSQL vs MySQL vs MongoDB vs Redis — 선택 기준과 실전 가이드
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- 1. 2025년 데이터베이스 랭킹과 트렌드
- 2. RDBMS vs NoSQL vs NewSQL — 언제 무엇을 써야 하는가
- 3. PostgreSQL 딥 다이브
- 4. MySQL 딥 다이브
- 5. MongoDB 딥 다이브
- 6. Redis 딥 다이브
- 7. DynamoDB — 서버리스 NoSQL의 대표
- 8. Cassandra / ScyllaDB — 대규모 쓰기의 왕
- 9. 성능 벤치마크 비교
- 10. 유스케이스별 선택 매트릭스
- 11. 멀티 DB 아키텍처 패턴
- 12. 비용 비교 (매니지드 서비스)
- 13. 면접 질문 20선
- 14. 퀴즈
- 참고 자료
들어가며
2025년 데이터베이스 생태계는 그 어느 때보다 다양하고 복잡합니다. AI/ML 워크로드의 폭발적 증가로 벡터 검색이 필수가 되었고, 실시간 스트리밍 처리, 멀티모델 지원, 서버리스 아키텍처 등 새로운 요구사항이 데이터베이스 선택을 더욱 어렵게 만들고 있습니다.
이 글에서는 PostgreSQL, MySQL, MongoDB, Redis를 중심으로 DynamoDB, Cassandra/ScyllaDB까지 8개 주요 데이터베이스의 아키텍처, 성능, 비용, 유스케이스를 체계적으로 비교합니다. 단순한 기능 나열이 아닌, 실제 프로덕션 환경에서의 의사결정에 도움이 되는 실전 가이드를 제공합니다.
1. 2025년 데이터베이스 랭킹과 트렌드
DB-Engines 랭킹 (2025년 3월 기준)
| 순위 | 데이터베이스 | 유형 | 점수 | 전년 대비 |
|---|---|---|---|---|
| 1 | Oracle | RDBMS | 1,245 | -3.2% |
| 2 | MySQL | RDBMS | 1,142 | -1.8% |
| 3 | PostgreSQL | RDBMS | 685 | +12.4% |
| 4 | MongoDB | Document | 397 | +2.1% |
| 5 | Redis | Key-Value | 168 | -5.3% |
| 6 | Elasticsearch | Search | 155 | +1.7% |
| 7 | SQLite | RDBMS | 142 | +8.9% |
| 8 | Cassandra | Wide-Column | 112 | -4.1% |
2025년 주요 트렌드
1. AI가 데이터베이스 선택을 바꾼다
RAG(Retrieval-Augmented Generation) 파이프라인이 보편화되면서 벡터 검색 기능이 데이터베이스 선택의 핵심 기준이 되었습니다. PostgreSQL의 pgvector, MongoDB Atlas Vector Search, Redis Stack이 이 경쟁에서 두각을 나타내고 있습니다.
2. 서버리스 데이터베이스의 부상
Neon(PostgreSQL 서버리스), PlanetScale(MySQL 서버리스), MongoDB Atlas Serverless, DynamoDB on-demand 등 사용량 기반 과금 모델이 표준이 되어가고 있습니다.
3. 멀티모델 데이터베이스
하나의 데이터베이스에서 관계형, 문서, 그래프, 시계열 데이터를 모두 처리하는 멀티모델 접근이 증가하고 있습니다. PostgreSQL이 JSONB, pgvector, PostGIS 등 확장으로 이 영역에서 가장 앞서가고 있습니다.
4. Valkey의 등장
Redis Labs의 라이선스 변경(SSPL)에 대응하여 Linux Foundation이 Valkey 포크를 출시했습니다. AWS ElastiCache, Google Cloud Memorystore 등이 Valkey를 채택하면서 Redis 생태계에 큰 변화가 일어나고 있습니다.
2. RDBMS vs NoSQL vs NewSQL — 언제 무엇을 써야 하는가
CAP 정리의 실전 이해
분산 시스템에서 Consistency(일관성), Availability(가용성), Partition Tolerance(분할 내성) 세 가지를 동시에 완벽하게 달성할 수 없다는 이론입니다.
Consistency (C)
/\
/ \
/ \
CP / CAP \ CA
시스템 / 불가능 \ 시스템
/________\
Partition(P) Availability(A)
| 유형 | 조합 | 데이터베이스 | 특징 |
|---|---|---|---|
| CA | 일관성 + 가용성 | 전통 RDBMS (단일 노드) | 네트워크 파티션 시 불가용 |
| CP | 일관성 + 분할내성 | MongoDB, HBase | 가용성 일부 포기 |
| AP | 가용성 + 분할내성 | Cassandra, DynamoDB | 최종적 일관성 |
RDBMS를 선택해야 할 때
- 트랜잭션 무결성이 필수 (금융, 결제, 재고 관리)
- 복잡한 JOIN과 집계 쿼리가 빈번
- 데이터 스키마가 명확하고 안정적
- ACID 준수가 법적/규제적 요구사항
NoSQL을 선택해야 할 때
- 스키마가 자주 변경되거나 비정형 데이터
- 수평 확장(horizontal scaling)이 핵심 요구사항
- 초저지연(sub-millisecond) 읽기/쓰기 필요
- 대용량 데이터의 빠른 쓰기 처리
NewSQL을 선택해야 할 때
CockroachDB, TiDB, YugabyteDB 같은 NewSQL 데이터베이스는 RDBMS의 트랜잭션 보장과 NoSQL의 수평 확장성을 모두 제공합니다.
- 글로벌 분산이 필요한 트랜잭션 시스템
- RDBMS 호환성을 유지하면서 수평 확장이 필요
- 다만, 성숙도와 커뮤니티 크기에서 전통 RDBMS에 비해 아직 부족
3. PostgreSQL 딥 다이브
아키텍처 개요
PostgreSQL은 프로세스 기반 아키텍처를 사용합니다. 각 클라이언트 연결마다 별도의 백엔드 프로세스가 할당됩니다.
┌─────────────────────────────────────────────┐
│ PostgreSQL │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Backend │ │ Backend │ │ Backend │ │
│ │ Process │ │ Process │ │ Process │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴─────────────┴─────────────┴────┐ │
│ │ Shared Memory │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Shared │ │ WAL │ │ │
│ │ │ Buffers │ │ Buffers │ │ │
│ │ └──────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Background Workers │ │
│ │ WAL Writer | Checkpointer | │ │
│ │ Autovacuum | Stats Collector │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
MVCC (Multi-Version Concurrency Control)
PostgreSQL의 MVCC는 각 행의 여러 버전을 유지하여 읽기/쓰기 간 잠금 충돌을 최소화합니다.
-- 트랜잭션 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- MVCC 동작 확인
BEGIN;
SELECT xmin, xmax, ctid, * FROM orders WHERE id = 1;
-- xmin: 생성 트랜잭션 ID, xmax: 삭제 트랜잭션 ID
-- ctid: 물리적 위치 (page, offset)
COMMIT;
JSONB — 관계형 + 문서형의 최강 조합
-- JSONB 컬럼 생성
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- GIN 인덱스 생성 (JSONB 검색 최적화)
CREATE INDEX idx_products_metadata ON products USING GIN (metadata);
-- JSONB 쿼리 예시
SELECT name, metadata->>'category' AS category,
(metadata->'specs'->>'weight')::numeric AS weight
FROM products
WHERE metadata @> '{"category": "electronics"}'
AND (metadata->'specs'->>'weight')::numeric < 500;
-- JSONB 집계
SELECT metadata->>'category' AS category,
COUNT(*),
AVG((metadata->'price'->>'amount')::numeric) AS avg_price
FROM products
GROUP BY metadata->>'category';
pgvector — AI/RAG 시대의 게임 체인저
-- pgvector 확장 설치
CREATE EXTENSION IF NOT EXISTS vector;
-- 벡터 컬럼이 있는 테이블
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
embedding vector(1536) -- OpenAI text-embedding-3-small 차원
);
-- HNSW 인덱스 (빠른 근사 최근접 이웃 검색)
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- IVFFlat 인덱스 (메모리 효율적)
CREATE INDEX ON documents
USING ivfflat (embedding vector_l2_ops)
WITH (lists = 100);
-- 코사인 유사도 기반 검색
SELECT id, title,
1 - (embedding <=> query_embedding) AS similarity
FROM documents
ORDER BY embedding <=> query_embedding
LIMIT 10;
-- 하이브리드 검색 (벡터 + 전문 검색)
SELECT id, title,
ts_rank(to_tsvector('korean', content), plainto_tsquery('korean', '검색어')) AS text_score,
1 - (embedding <=> query_embedding) AS vector_score
FROM documents
WHERE to_tsvector('korean', content) @@ plainto_tsquery('korean', '검색어')
ORDER BY vector_score * 0.7 + text_score * 0.3 DESC
LIMIT 10;
pg_trgm — 퍼지 검색과 유사도 매칭
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- 트라이그램 인덱스
CREATE INDEX idx_products_name_trgm ON products
USING GIN (name gin_trgm_ops);
-- 유사도 검색 (오타 허용)
SELECT name, similarity(name, 'PostgreSLQ') AS sim
FROM products
WHERE name % 'PostgreSLQ'
ORDER BY sim DESC;
파티셔닝
-- 범위 파티셔닝 예시
CREATE TABLE orders (
id BIGSERIAL,
order_date DATE NOT NULL,
customer_id INTEGER NOT NULL,
total_amount NUMERIC(10,2),
PRIMARY KEY (id, order_date)
) PARTITION BY RANGE (order_date);
-- 파티션 생성
CREATE TABLE orders_2025_q1 PARTITION OF orders
FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');
CREATE TABLE orders_2025_q2 PARTITION OF orders
FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');
-- 자동 파티션 관리 (pg_partman)
CREATE EXTENSION pg_partman;
SELECT partman.create_parent(
p_parent_table => 'public.orders',
p_control => 'order_date',
p_type => 'native',
p_interval => '3 months'
);
PostgreSQL 17 주요 기능
- Incremental Backup: pg_basebackup에서 증분 백업 지원
- SQL/JSON 표준 지원 강화: JSON_TABLE, JSON_QUERY 등
- 성능 향상: 병렬 해시 조인 개선, 벌크 로딩 최적화
- 보안 강화: LDAP/SCRAM 인증 개선
4. MySQL 딥 다이브
InnoDB 아키텍처
┌─────────────────────────────────────────────┐
│ MySQL Server │
│ ┌───────────────────────────────────────┐ │
│ │ Connection Pool │ │
│ │ Thread 1 | Thread 2 | Thread N │ │
│ └───────────────────────────────────────┘ │
│ ┌───────────────────────────────────────┐ │
│ │ Query Cache (제거됨 8.0+) │ │
│ │ Parser -> Optimizer │ │
│ └───────────────────────────────────────┘ │
│ ┌───────────────────────────────────────┐ │
│ │ InnoDB Engine │ │
│ │ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Buffer │ │ Change Buffer │ │ │
│ │ │ Pool │ │ │ │ │
│ │ └──────────┘ └──────────────────┘ │ │
│ │ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │ Redo Log │ │ Undo Log │ │ │
│ │ └──────────┘ └──────────────────┘ │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
MySQL은 스레드 기반 아키텍처로, PostgreSQL의 프로세스 기반과 대비됩니다. 이로 인해 연결 오버헤드가 낮고 메모리 효율이 높습니다.
MySQL vs PostgreSQL 핵심 차이
-- MySQL: UPSERT (INSERT ... ON DUPLICATE KEY)
INSERT INTO products (id, name, stock)
VALUES (1, 'Widget', 100)
ON DUPLICATE KEY UPDATE stock = stock + VALUES(stock);
-- PostgreSQL: UPSERT (INSERT ... ON CONFLICT)
INSERT INTO products (id, name, stock)
VALUES (1, 'Widget', 100)
ON CONFLICT (id)
DO UPDATE SET stock = products.stock + EXCLUDED.stock;
HeatWave ML — MySQL에서 직접 ML
-- HeatWave ML로 모델 학습
CALL sys.ML_TRAIN('my_schema.customer_data',
'churn_label',
JSON_OBJECT('task', 'classification'),
@model_handle);
-- 예측 실행
CALL sys.ML_PREDICT_ROW(
JSON_OBJECT('age', 35, 'tenure', 24, 'monthly_charge', 79.99),
@model_handle,
@prediction);
SELECT @prediction;
Group Replication (그룹 복제)
MySQL Group Replication은 Paxos 기반 합의 프로토콜을 사용하여 고가용성을 제공합니다.
-- Group Replication 상태 확인
SELECT MEMBER_ID, MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
-- 단일 프라이머리 모드 (기본, 권장)
-- 하나의 쓰기 노드, 나머지는 읽기 전용
SET GLOBAL group_replication_single_primary_mode = ON;
ProxySQL로 읽기/쓰기 분리
-- ProxySQL 라우팅 룰 설정
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup)
VALUES
(1, 1, '^SELECT .* FOR UPDATE', 0), -- 쓰기 그룹
(2, 1, '^SELECT', 1); -- 읽기 그룹
-- 서버 그룹 설정
INSERT INTO mysql_servers (hostgroup_id, hostname, port, weight)
VALUES
(0, 'primary.mysql.local', 3306, 1), -- 쓰기
(1, 'replica1.mysql.local', 3306, 50), -- 읽기
(1, 'replica2.mysql.local', 3306, 50); -- 읽기
MySQL 9.0 주요 기능
- JavaScript Stored Programs: GraalVM 기반 JavaScript 저장 프로시저
- Vector Data Type: 벡터 데이터 타입 네이티브 지원
- EXPLAIN 개선: FORMAT=TREE 기본값, 더 직관적인 실행 계획
- 보안: OpenID Connect 인증 지원
5. MongoDB 딥 다이브
문서 모델의 강점
// MongoDB 문서 구조 — 임베딩 패턴
db.orders.insertOne({
orderId: 'ORD-2025-001',
customer: {
name: '김개발',
email: 'kim@example.com',
address: {
street: '강남대로 123',
city: '서울',
zipCode: '06123',
},
},
items: [
{
productId: 'PROD-001',
name: '무선 키보드',
price: 89000,
quantity: 2,
},
{
productId: 'PROD-002',
name: '모니터 스탠드',
price: 45000,
quantity: 1,
},
],
payment: {
method: 'card',
status: 'completed',
paidAt: ISODate('2025-03-15T10:30:00Z'),
},
totalAmount: 223000,
createdAt: ISODate('2025-03-15T10:25:00Z'),
})
Atlas Vector Search
// 벡터 검색 인덱스 생성
db.collection.createSearchIndex({
name: 'vector_index',
definition: {
mappings: {
dynamic: true,
fields: {
embedding: {
type: 'knnVector',
dimensions: 1536,
similarity: 'cosine',
},
},
},
},
})
// 벡터 검색 쿼리
db.documents.aggregate([
{
$vectorSearch: {
index: 'vector_index',
path: 'embedding',
queryVector: queryEmbedding, // 1536차원 벡터
numCandidates: 100,
limit: 10,
filter: { category: 'tech' },
},
},
{
$project: {
title: 1,
content: 1,
score: { $meta: 'vectorSearchScore' },
},
},
])
Aggregation Pipeline 고급 예시
// 월별 매출 분석 파이프라인
db.orders.aggregate([
// 기간 필터
{
$match: {
createdAt: {
$gte: ISODate('2025-01-01'),
$lt: ISODate('2026-01-01'),
},
'payment.status': 'completed',
},
},
// 주문 항목 분해
{ $unwind: '$items' },
// 월별 그룹핑
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
category: '$items.category',
},
totalRevenue: { $sum: { $multiply: ['$items.price', '$items.quantity'] } },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: { $multiply: ['$items.price', '$items.quantity'] } },
},
},
// 정렬
{ $sort: { '_id.year': 1, '_id.month': 1 } },
// 출력 형태 변환
{
$project: {
_id: 0,
period: { $concat: [{ $toString: '$_id.year' }, '-', { $toString: '$_id.month' }] },
category: '$_id.category',
totalRevenue: 1,
orderCount: 1,
avgOrderValue: { $round: ['$avgOrderValue', 0] },
},
},
])
MongoDB 샤딩
// 샤드 키 선택 — 카디널리티, 분산도, 쿼리 패턴 고려
sh.enableSharding('ecommerce')
// 해시 샤딩 (균등 분산)
sh.shardCollection('ecommerce.orders', { customerId: 'hashed' })
// 범위 샤딩 (범위 쿼리 최적화)
sh.shardCollection('ecommerce.logs', { timestamp: 1 })
// 존 샤딩 (지역 기반 데이터 분리)
sh.addShardTag('shard-kr', 'ASIA')
sh.addShardTag('shard-us', 'AMERICAS')
sh.addTagRange('ecommerce.users', { region: 'KR' }, { region: 'KS' }, 'ASIA')
MongoDB 8.0 주요 기능
- Queryable Encryption: 암호화된 상태에서 쿼리 가능
- Cluster-to-Cluster Sync: 멀티 클라우드 동기화
- Time Series Collection 개선: 더 효율적인 시계열 데이터 처리
- 성능: 읽기/쓰기 성능 20-35% 향상
6. Redis 딥 다이브
데이터 구조와 활용
-- Strings: 캐시, 세션, 카운터
SET user:session:abc123 "session_data" EX 3600
INCR page:views:homepage
-- Hashes: 객체 저장
HSET user:1001 name "김개발" email "kim@dev.com" login_count 42
HINCRBY user:1001 login_count 1
-- Sorted Sets: 리더보드, 랭킹
ZADD leaderboard 9500 "player:alice"
ZADD leaderboard 8700 "player:bob"
ZADD leaderboard 9200 "player:charlie"
ZREVRANGE leaderboard 0 9 WITHSCORES -- Top 10
-- Streams: 이벤트 스트리밍
XADD mystream * sensor_id "temp-01" temperature "23.5" humidity "45"
XREAD COUNT 10 BLOCK 5000 STREAMS mystream 0
-- HyperLogLog: 고유 카운트 (근사치)
PFADD unique:visitors:2025-03-23 "user1" "user2" "user3"
PFCOUNT unique:visitors:2025-03-23
Redis Stack — Search, JSON, TimeSeries
-- Redis JSON
JSON.SET product:001 $ '{"name":"무선 키보드","price":89000,"tags":["electronics","keyboard"],"specs":{"weight":350,"wireless":true}}'
JSON.GET product:001 $.specs.weight
-- Redis Search 인덱스 생성
FT.CREATE idx:products ON JSON PREFIX 1 product:
SCHEMA
$.name AS name TEXT SORTABLE
$.price AS price NUMERIC SORTABLE
$.tags[*] AS tags TAG
$.specs.weight AS weight NUMERIC
-- 전문 검색
FT.SEARCH idx:products "@name:키보드 @price:[50000 100000]"
-- 벡터 검색 (Redis Stack)
FT.CREATE idx:docs ON HASH PREFIX 1 doc:
SCHEMA
title TEXT
embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE
-- KNN 검색
FT.SEARCH idx:docs "*=>[KNN 10 @embedding $query_vec AS score]"
PARAMS 2 query_vec "\x00\x00..."
SORTBY score
RETURN 2 title score
-- Redis TimeSeries
TS.CREATE temperature:sensor1 RETENTION 86400000 LABELS sensor_id temp-01 location office
TS.ADD temperature:sensor1 * 23.5
TS.RANGE temperature:sensor1 - + AGGREGATION avg 60000 -- 1분 평균
영속성: RDB vs AOF
| 특성 | RDB (스냅샷) | AOF (Append Only File) |
|---|---|---|
| 동작 방식 | 주기적 메모리 덤프 | 모든 쓰기 명령 로깅 |
| 데이터 손실 | 마지막 스냅샷 이후 | fsync 정책에 따라 다름 |
| 복구 속도 | 빠름 (바이너리 로드) | 느림 (명령 재실행) |
| 파일 크기 | 작음 (압축) | 큼 (모든 명령 기록) |
| CPU 영향 | fork() 시 높음 | everysec 시 낮음 |
# redis.conf 권장 설정
save 900 1 # 900초 동안 1개 이상 변경 시 RDB
save 300 10 # 300초 동안 10개 이상 변경 시 RDB
appendonly yes # AOF 활성화
appendfsync everysec # 1초마다 fsync (성능/안전 균형)
Redis Cluster
# 6노드 클러스터 생성 (3 마스터 + 3 레플리카)
redis-cli --cluster create \
node1:6379 node2:6379 node3:6379 \
node4:6379 node5:6379 node6:6379 \
--cluster-replicas 1
Redis Cluster는 16384개의 해시 슬롯을 사용하여 키를 분산합니다. 키 공간은 CRC16 해시를 사용하여 슬롯에 매핑됩니다.
Valkey 포크 — Redis의 미래?
2024년 Redis Labs가 SSPL 라이선스로 전환한 후, Linux Foundation 주도로 Valkey가 탄생했습니다.
| 항목 | Redis | Valkey |
|---|---|---|
| 라이선스 | SSPL (2024~) | BSD-3-Clause |
| 지원 클라우드 | Redis Cloud | AWS, GCP, Azure |
| 호환성 | 원본 | Redis 7.2 호환 |
| 개발 주체 | Redis Ltd. | Linux Foundation |
| 커뮤니티 | 제한적 기여 | 오픈 기여 |
7. DynamoDB — 서버리스 NoSQL의 대표
핵심 개념
┌───────────────────────────────────────────┐
│ DynamoDB Table │
│ ┌─────────────────────────────────────┐ │
│ │ Partition Key (PK) Sort Key (SK) │ │
│ ├─────────────────────────────────────┤ │
│ │ USER#001 PROFILE │ │
│ │ USER#001 ORDER#001 │ │
│ │ USER#001 ORDER#002 │ │
│ │ USER#002 PROFILE │ │
│ │ USER#002 ORDER#001 │ │
│ └─────────────────────────────────────┘ │
│ │
│ GSI: OrderDate-Index │
│ ┌─────────────────────────────────────┐ │
│ │ OrderDate (PK) UserId (SK) │ │
│ └─────────────────────────────────────┘ │
└───────────────────────────────────────────┘
Single-Table Design 패턴
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('EcommerceTable')
# 사용자 프로필 저장
table.put_item(Item={
'PK': 'USER#001',
'SK': 'PROFILE',
'name': '김개발',
'email': 'kim@dev.com',
'type': 'USER'
})
# 같은 테이블에 주문 저장
table.put_item(Item={
'PK': 'USER#001',
'SK': 'ORDER#2025-03-15#ORD001',
'items': [{'name': '키보드', 'price': 89000}],
'total': 89000,
'status': 'completed',
'type': 'ORDER'
})
# 사용자의 모든 주문 조회 (Query)
response = table.query(
KeyConditionExpression='PK = :pk AND begins_with(SK, :sk)',
ExpressionAttributeValues={
':pk': 'USER#001',
':sk': 'ORDER#'
}
)
DynamoDB 가격 모델
| 모드 | 읽기 비용 | 쓰기 비용 | 적합한 경우 |
|---|---|---|---|
| On-Demand | 0.25 USD/100만 RRU | 1.25 USD/100만 WRU | 트래픽 예측 불가 |
| Provisioned | 0.00065 USD/RCU/시간 | 0.00065 USD/WCU/시간 | 안정적 트래픽 |
| Reserved | 최대 77% 할인 | 최대 77% 할인 | 1년/3년 약정 |
8. Cassandra / ScyllaDB — 대규모 쓰기의 왕
Cassandra 아키텍처
Cassandra는 Dynamo 스타일의 분산 해시 테이블과 BigTable 스타일의 데이터 모델을 결합합니다.
┌────────┐ ┌────────┐ ┌────────┐
│ Node A │──│ Node B │──│ Node C │
│ Token: │ │ Token: │ │ Token: │
│ 0-33 │ │ 34-66 │ │ 67-100 │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└───────────┼───────────┘
Gossip Protocol
-- 키스페이스 생성 (복제 전략 포함)
CREATE KEYSPACE ecommerce WITH replication = {
'class': 'NetworkTopologyStrategy',
'us-east': 3,
'eu-west': 3
};
-- 테이블 생성 (파티션 키 + 클러스터링 키)
CREATE TABLE ecommerce.user_activity (
user_id UUID,
activity_date DATE,
activity_time TIMESTAMP,
activity_type TEXT,
details MAP<TEXT, TEXT>,
PRIMARY KEY ((user_id, activity_date), activity_time)
) WITH CLUSTERING ORDER BY (activity_time DESC)
AND compaction = {'class': 'TimeWindowCompactionStrategy',
'compaction_window_size': 1,
'compaction_window_unit': 'DAYS'};
ScyllaDB — Cassandra 호환 고성능 대안
ScyllaDB는 C++로 재작성된 Cassandra 호환 데이터베이스입니다. Shard-per-core 아키텍처로 동일 하드웨어에서 3-10배 높은 성능을 제공합니다.
| 비교 항목 | Cassandra | ScyllaDB |
|---|---|---|
| 언어 | Java | C++ (Seastar) |
| 아키텍처 | 스레드 기반 | Shard-per-core |
| GC 영향 | GC pause 존재 | GC 없음 |
| 같은 하드웨어 성능 | 기준 | 3-10x |
| Cassandra 호환 | 원본 | CQL 호환 |
9. 성능 벤치마크 비교
YCSB 벤치마크 (Yahoo Cloud Serving Benchmark)
다음은 동일 하드웨어(8 vCPU, 32GB RAM, NVMe SSD)에서의 비교입니다.
| 작업 | PostgreSQL | MySQL | MongoDB | Redis | DynamoDB | ScyllaDB |
|---|---|---|---|---|---|---|
| 읽기 지연 (p99) | 2.1ms | 1.8ms | 1.5ms | 0.3ms | 4.2ms | 0.9ms |
| 쓰기 지연 (p99) | 3.5ms | 2.9ms | 2.1ms | 0.4ms | 5.8ms | 1.2ms |
| 읽기 TPS | 45K | 52K | 58K | 250K | 25K | 120K |
| 쓰기 TPS | 28K | 35K | 42K | 200K | 20K | 85K |
| 혼합 (50/50) TPS | 35K | 42K | 48K | 220K | 22K | 100K |
주의: 벤치마크 결과는 워크로드 패턴, 데이터 크기, 인덱스 구성 등에 따라 크게 달라집니다.
확장성 비교
| 확장 방식 | PostgreSQL | MySQL | MongoDB | Redis | DynamoDB | Cassandra |
|---|---|---|---|---|---|---|
| 수직 확장 | 우수 | 우수 | 좋음 | 좋음 | N/A | 좋음 |
| 읽기 확장 | 복제본 | 복제본 | 복제본 세트 | 클러스터 | 자동 | 노드 추가 |
| 쓰기 확장 | 제한적 | 제한적 | 샤딩 | 클러스터 | 자동 | 노드 추가 |
| 최대 데이터 크기 | 수 TB | 수 TB | 수 PB | 수백 GB | 무제한 | 수 PB |
10. 유스케이스별 선택 매트릭스
최적 데이터베이스 선택 가이드
| 유스케이스 | 1순위 | 2순위 | 이유 |
|---|---|---|---|
| 이커머스 | PostgreSQL | MySQL | 트랜잭션 + JSONB 유연성 |
| 소셜 미디어 | MongoDB | Cassandra | 비정형 데이터 + 높은 쓰기 |
| IoT 센서 데이터 | ScyllaDB | DynamoDB | 대용량 시계열 쓰기 |
| 실시간 분석 | PostgreSQL | ClickHouse | 복잡한 집계 쿼리 |
| RAG/AI 검색 | PostgreSQL (pgvector) | MongoDB (Atlas Vector) | 벡터 + 전문 검색 |
| 캐시 | Redis | DragonflyDB | 초저지연, 풍부한 데이터 구조 |
| 세션 저장소 | Redis | DynamoDB | 빠른 읽기/쓰기, TTL |
| 채팅/메시징 | MongoDB | Cassandra | 유연한 스키마, 시간순 정렬 |
| 게임 리더보드 | Redis | DynamoDB | Sorted Set, 빠른 갱신 |
| 로그 관리 | Elasticsearch | MongoDB | 전문 검색, 시계열 |
| 지리 공간 | PostgreSQL (PostGIS) | MongoDB | GIS 기능, 공간 인덱스 |
| 그래프 데이터 | Neo4j | PostgreSQL (AGE) | 관계 탐색 최적화 |
11. 멀티 DB 아키텍처 패턴
Polyglot Persistence (다중 데이터베이스 전략)
┌─────────────────────────────────────────────────┐
│ Application Layer │
└───────┬──────────┬──────────┬──────────┬────────┘
│ │ │ │
┌────▼────┐ ┌──▼───┐ ┌───▼───┐ ┌───▼────┐
│PostgreSQL│ │Redis │ │MongoDB│ │Elastic │
│(주문/결제)│ │(캐시) │ │(상품) │ │(검색) │
└─────────┘ └──────┘ └───────┘ └────────┘
CQRS 패턴 (Command Query Responsibility Segregation)
쓰기 (Command): 읽기 (Query):
┌──────────┐ ┌──────────┐
│ Client │ │ Client │
└────┬─────┘ └────┬─────┘
│ │
┌────▼─────┐ ┌────▼─────┐
│ Command │ │ Query │
│ Service │ │ Service │
└────┬─────┘ └────┬─────┘
│ │
┌────▼─────┐ ──이벤트──▶ ┌─────▼─────┐
│PostgreSQL│ │ Redis / │
│ (Source │ │ MongoDB │
│ of Truth)│ │ (Read Model)│
└──────────┘ └───────────┘
Cache-Aside 패턴
import redis
import psycopg2
import json
r = redis.Redis(host='localhost', port=6379)
def get_product(product_id):
# 1. 캐시 확인
cache_key = f"product:{product_id}"
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# 2. 캐시 미스 — DB 조회
conn = psycopg2.connect("dbname=ecommerce")
cur = conn.cursor()
cur.execute("SELECT * FROM products WHERE id = %s", (product_id,))
product = cur.fetchone()
if product:
# 3. 캐시에 저장 (TTL 1시간)
r.setex(cache_key, 3600, json.dumps(product))
return product
def update_product(product_id, data):
# 1. DB 업데이트
conn = psycopg2.connect("dbname=ecommerce")
cur = conn.cursor()
cur.execute("UPDATE products SET name=%s WHERE id=%s",
(data['name'], product_id))
conn.commit()
# 2. 캐시 무효화
r.delete(f"product:{product_id}")
12. 비용 비교 (매니지드 서비스)
월간 비용 예상 (중간 규모 서비스: 100GB 데이터, 5000 TPS)
| 서비스 | 제공자 | 인스턴스 | 월 비용 (USD) |
|---|---|---|---|
| RDS PostgreSQL | AWS | db.r6g.xlarge | ~350 |
| Cloud SQL PostgreSQL | GCP | db-custom-4-16384 | ~320 |
| RDS MySQL | AWS | db.r6g.xlarge | ~350 |
| Atlas MongoDB | MongoDB | M40 | ~480 |
| ElastiCache Redis | AWS | cache.r6g.xlarge | ~450 |
| DynamoDB On-Demand | AWS | - | ~180-600 |
| Neon PostgreSQL | Neon | Scale | ~69-189 |
| PlanetScale MySQL | PlanetScale | Scaler Pro | ~39-299 |
비용 최적화 전략
- 예약 인스턴스: 1-3년 약정으로 40-70% 할인
- 서버리스 옵션: 트래픽 변동이 큰 경우 Neon, PlanetScale, DynamoDB on-demand
- 읽기 전용 복제본: 읽기 부하 분산으로 메인 인스턴스 스펙 절약
- 적절한 인스턴스 크기: CloudWatch/모니터링으로 실제 사용률 확인 후 조정
- 데이터 아카이빙: 오래된 데이터를 S3/Glacier로 이전
13. 면접 질문 20선
기본 개념 (1-5)
Q1: ACID와 BASE의 차이를 설명하세요.
ACID (Atomicity, Consistency, Isolation, Durability): 전통적 RDBMS의 트랜잭션 속성. 모든 작업이 완전히 성공하거나 완전히 실패하며, 데이터 일관성을 보장합니다.
BASE (Basically Available, Soft state, Eventually consistent): NoSQL의 접근 방식. 항상 사용 가능하지만 즉각적 일관성 대신 최종적 일관성을 보장합니다.
선택 기준: 금융/결제 시스템에는 ACID, SNS 피드/좋아요 카운터 같은 곳에는 BASE가 적합합니다.
Q2: CAP 정리를 실무 예시와 함께 설명하세요.
- CP 시스템 (MongoDB): 네트워크 파티션 발생 시 일관성을 위해 일부 노드의 가용성을 포기. 프라이머리 선출 중 쓰기 불가.
- AP 시스템 (Cassandra, DynamoDB): 네트워크 파티션에서도 모든 노드가 응답하지만, 노드 간 데이터가 일시적으로 다를 수 있음.
- CA 시스템: 네트워크 파티션이 없는 단일 노드 시스템. 분산 환경에서는 사실상 존재하지 않음.
실무 팁: PACELC 정리로 확장하면, 파티션이 없을 때(Else) 지연(Latency)과 일관성(Consistency) 사이의 트레이드오프도 고려합니다.
Q3: 인덱스의 종류와 각각의 적합한 사용 사례를 설명하세요.
- B-Tree 인덱스: 범위 검색, 정렬에 최적. 대부분의 기본 인덱스. O(log N)
- Hash 인덱스: 동등 비교(=)에만 사용. O(1). PostgreSQL에서는 WAL 미지원 문제로 잘 안 쓰임
- GIN (Generalized Inverted Index): 배열, JSONB, 전문 검색에 적합
- GiST (Generalized Search Tree): 공간 데이터(PostGIS), 범위 타입에 적합
- BRIN (Block Range Index): 물리적으로 정렬된 대용량 테이블(시계열)에 효과적. 메모리 효율적
- Bloom 인덱스: 많은 컬럼에 대한 동등 비교 필터링
Q4: N+1 쿼리 문제가 무엇이고 어떻게 해결하나요?
N+1 문제: 1번의 쿼리로 N개의 결과를 가져온 후, 각 결과에 대해 추가 쿼리를 N번 실행하는 비효율적 패턴.
해결 방법:
- JOIN: 관계형 DB에서 한 번의 쿼리로 데이터 조인
- Eager Loading: ORM에서 관련 데이터를 미리 로드 (Django:
select_related,prefetch_related) - DataLoader 패턴: GraphQL에서 배치 요청으로 묶어 처리
- MongoDB: 임베딩 패턴으로 관련 데이터를 하나의 문서에 저장
Q5: 데이터베이스 정규화와 비정규화의 트레이드오프를 설명하세요.
정규화 (1NF-BCNF):
- 장점: 데이터 중복 최소화, 일관성 유지 용이, 저장 공간 절약
- 단점: JOIN 증가로 읽기 성능 저하, 복잡한 쿼리
비정규화:
- 장점: 읽기 성능 향상 (JOIN 감소), 단순한 쿼리
- 단점: 데이터 중복, 갱신 이상(anomaly) 위험, 저장 공간 증가
실무 가이드: OLTP는 3NF 이상 정규화, OLAP/읽기 집중 워크로드는 의도적 비정규화 검토.
심화 문제 (6-10)
Q6: PostgreSQL의 MVCC와 MySQL InnoDB의 MVCC 차이를 설명하세요.
PostgreSQL MVCC:
- 각 행의 여러 버전을 테이블에 직접 저장 (xmin, xmax)
- 오래된 버전은 VACUUM으로 정리
- 장점: 읽기 시 잠금 없음, 높은 동시성
- 단점: 테이블 팽창(bloat), VACUUM 오버헤드
MySQL InnoDB MVCC:
- Undo Log에 이전 버전 저장
- 현재 행에는 항상 최신 버전
- Purge 스레드가 불필요한 Undo 정리
- 장점: 테이블 팽창 없음, 간단한 관리
- 단점: Undo 로그 크기 증가 시 성능 저하
Q7: 데이터베이스 샤딩 전략의 종류와 선택 기준은?
- 해시 샤딩: 키를 해시하여 균등 분산. 범위 쿼리 비효율적.
- 범위 샤딩: 키 범위로 분할. 핫스팟 위험. 시계열에 적합.
- 지리 기반 샤딩: 사용자 위치 기반 분할. 지역별 규제 준수에 유용.
- 디렉토리 기반 샤딩: 매핑 테이블로 유연한 라우팅. 관리 복잡도 높음.
샤드 키 선택 기준: 높은 카디널리티, 균등 분산, 쿼리 패턴과 일치.
Q8: Redis가 싱글 스레드인데 어떻게 빠른가요?
- 인메모리 처리: 모든 데이터가 RAM에 있어 디스크 I/O 없음
- I/O 멀티플렉싱: epoll/kqueue로 수천 연결을 단일 스레드로 처리
- 잠금 불필요: 싱글 스레드이므로 동시성 제어 오버헤드 없음
- 최적화된 자료 구조: SDS, ziplist, intset 등 메모리 효율적 구조
- Redis 6.0+: I/O 스레딩 도입 (명령 처리는 여전히 싱글 스레드)
참고: Redis 7.0+에서는 멀티스레드 I/O가 더 개선되어 네트워크 처리 성능이 향상되었습니다.
Q9: MongoDB에서 임베딩과 레퍼런싱 중 어떤 것을 선택해야 하나요?
임베딩 (하위 문서):
- 함께 조회되는 데이터
- 1:1 또는 1:소수 관계
- 16MB 문서 크기 제한 내
- 예: 주문과 주문 항목, 사용자와 주소
레퍼런싱 (별도 컬렉션):
- 독립적으로 조회/업데이트되는 데이터
- 1:다수 또는 다:다 관계
- 문서 크기가 빠르게 증가하는 경우
- 예: 블로그 글과 댓글, 상품과 리뷰
하이브리드 패턴: 자주 쓰는 데이터는 임베딩, 나머지는 레퍼런싱.
Q10: 커넥션 풀링이 왜 중요하고 어떻게 설정하나요?
DB 연결 생성 비용: TCP 핸드셰이크 + 인증 + 세션 할당 등으로 수~수십 ms 소요. 매 요청마다 연결을 생성하면 성능이 크게 저하됩니다.
설정 예시 (HikariCP):
maximumPoolSize: 일반적으로 CPU 코어 수 * 2 + 디스크 수minimumIdle: 최소 유휴 연결 수connectionTimeout: 풀에서 연결을 기다리는 최대 시간idleTimeout: 유휴 연결 유지 시간
PostgreSQL은 프로세스 기반이므로 PgBouncer를 앞에 두어 연결 풀링을 관리하는 것이 모범 사례입니다.
실전/시스템 설계 (11-15)
Q11: 대규모 트래픽 전자상거래 서비스의 DB 아키텍처를 설계하세요.
- 상품 카탈로그: MongoDB (유연한 속성, 빠른 읽기)
- 주문/결제: PostgreSQL (ACID 트랜잭션)
- 장바구니/세션: Redis (TTL, 빠른 읽기/쓰기)
- 상품 검색: Elasticsearch (전문 검색, 패싯)
- 추천 시스템: PostgreSQL pgvector (벡터 유사도)
- 분석/리포팅: ClickHouse 또는 BigQuery
캐싱 전략: Redis에서 Cache-Aside 패턴으로 상품 정보 캐싱. CDN으로 정적 콘텐츠 캐싱.
Q12: 데이터베이스 마이그레이션 전략을 설명하세요.
- Dual-Write 패턴: 양쪽 DB에 동시 쓰기, 점진적 읽기 전환
- CDC (Change Data Capture): Debezium 등으로 실시간 데이터 동기화
- Shadow Traffic: 새 DB에 트래픽 미러링하여 성능 검증
- Blue-Green: 두 환경을 준비하고 한 번에 전환
핵심 포인트: 롤백 계획 필수, 데이터 일관성 검증 자동화, 점진적 트래픽 이전.
Q13: 읽기 복제본과 쓰기 마스터의 복제 지연(replication lag) 문제를 어떻게 해결하나요?
- Read-your-writes 일관성: 쓰기 후 짧은 시간 동안 마스터에서 읽기
- 최소 복제 확인: PostgreSQL의
synchronous_commit설정 - 지연 모니터링:
pg_stat_replication의replay_lag체크 - GTID 기반 라우팅: MySQL GTID로 특정 시점까지 반영된 복제본으로 라우팅
- 캐시 활용: 쓰기 직후 캐시에 최신 값 저장, 복제본 대신 캐시에서 읽기
Q14: 대용량 테이블에서 스키마 변경을 어떻게 안전하게 수행하나요?
PostgreSQL:
ALTER TABLE ADD COLUMN(DEFAULT 없이): 즉시 완료ALTER TABLE ADD COLUMN DEFAULT value: pg 11+ 즉시 완료CREATE INDEX CONCURRENTLY: 테이블 잠금 없이 인덱스 생성
MySQL:
pt-online-schema-change(Percona): 트리거 기반 온라인 변경gh-ost(GitHub): 바이너리 로그 기반 온라인 변경- MySQL 8.0+
INSTANTDDL: 메타데이터만 변경하는 작업 지원
MongoDB:
- 스키마리스이므로 대부분 마이그레이션 불필요
- 인덱스 생성:
createIndex(백그라운드 빌드 기본)
Q15: DB 성능 모니터링에서 가장 중요한 지표는?
- 쿼리 성능: 슬로우 쿼리 로그, p95/p99 응답 시간
- 연결 수: 활성/유휴 연결, 커넥션 풀 사용률
- 캐시 히트율: Buffer Pool/Shared Buffer 히트율 (목표: 99% 이상)
- 복제 지연: 마스터-레플리카 간 지연 시간
- 디스크 I/O: IOPS, 대기 시간, 사용률
- 잠금 대기: 행 잠금/테이블 잠금 대기 시간과 빈도
- 트랜잭션 처리량: TPS, 커밋/롤백 비율
도구: pg_stat_statements(PostgreSQL), Performance Schema(MySQL), Atlas(MongoDB), Redis INFO.
심화 실전 문제 (16-20)
Q16: Hot Partition 문제란 무엇이고 어떻게 해결하나요?
특정 파티션/샤드에 트래픽이 집중되어 성능 병목이 발생하는 문제입니다.
DynamoDB 예시: 특정 파티션 키에 쓰기가 집중되면 해당 파티션만 스로틀링 발생.
해결 방법:
- Write Sharding: 키에 랜덤 접미사 추가 (예:
USER#001#3) - Composite Key: 날짜/시간 등을 키에 포함하여 분산
- 캐싱 레이어: 읽기 핫스팟은 Redis로 흡수
- 분산 카운터: 하나의 카운터를 N개로 분할하여 합산
Q17: 데이터베이스 백업 전략의 종류와 RPO/RTO를 설명하세요.
- RPO (Recovery Point Objective): 허용 가능한 최대 데이터 손실 시간
- RTO (Recovery Time Objective): 허용 가능한 최대 복구 시간
백업 종류:
- Full Backup: 전체 데이터. RTO 짧음, 저장 공간 큼
- Incremental Backup: 마지막 백업 이후 변경분. 저장 공간 적음, 복구 복잡
- Continuous Archiving (WAL): PostgreSQL WAL 아카이빙으로 특정 시점 복구(PITR) 가능. RPO 거의 0
- 스냅샷: EBS 스냅샷, RDS 자동 백업. 빠른 복구
Q18: Slow Query를 분석하고 최적화하는 프로세스를 설명하세요.
- 식별: 슬로우 쿼리 로그 활성화 (MySQL:
slow_query_log, PostgreSQL:log_min_duration_statement) - 분석: EXPLAIN ANALYZE로 실행 계획 확인
- 최적화 순서:
- 인덱스 추가/개선
- 쿼리 리팩토링 (불필요한 JOIN 제거, 서브쿼리를 CTE로)
- 파티셔닝 적용
- 데이터 아카이빙
- 비정규화 검토 (읽기 최적화)
- 검증: 변경 전후 성능 비교
- 모니터링: pg_stat_statements, Performance Schema 지속 관찰
Q19: Eventually Consistent 시스템에서 데이터 정합성을 어떻게 보장하나요?
- Read Repair: 읽기 시 불일치 발견하면 최신 값으로 수정
- Anti-Entropy (Merkle Tree): 백그라운드에서 노드 간 데이터 비교/수정
- Conflict Resolution: 최종 쓰기 승리(LWW), 벡터 클록, 커스텀 병합 로직
- Quorum 읽기/쓰기: R + W > N 조건으로 강한 일관성 확보
- Saga 패턴: 분산 트랜잭션 대신 보상 트랜잭션으로 일관성 유지
Q20: 2025년 DB 선택 시 가장 중요하게 고려해야 할 요소 3가지는?
-
AI/벡터 검색 지원: RAG 파이프라인이 표준이 되면서, 벡터 검색 기능이 필수. pgvector, Atlas Vector Search, Redis Vector Search 중 선택.
-
운영 복잡도 vs 비용: 매니지드 서비스(RDS, Atlas, ElastiCache)의 편리함 vs 셀프 호스팅의 비용 절감. 팀 규모와 DevOps 역량에 따라 결정.
-
데이터 지역성과 규제: GDPR, 한국 개인정보보호법 등 데이터 주권 규제. DynamoDB Global Tables, CockroachDB의 지역 기반 분산이 해답이 될 수 있음.
14. 퀴즈
퀴즈 1: PostgreSQL에서 JSONB와 JSON 타입의 주요 차이점 2가지는?
- 저장 방식: JSON은 텍스트 그대로 저장, JSONB는 바이너리 분해하여 저장. JSONB가 쓰기는 약간 느리지만 읽기/검색이 훨씬 빠름.
- 인덱싱: JSONB는 GIN 인덱스를 지원하여 키/값 검색이 O(log N). JSON은 인덱스 불가능.
추가: JSONB는 키 순서를 보장하지 않고 중복 키를 제거합니다.
퀴즈 2: Redis의 Sorted Set이 내부적으로 사용하는 자료구조는?
Skip List + Hash Table 조합입니다.
- Skip List: 정렬된 순서로 범위 검색 O(log N)
- Hash Table: 점수 조회 O(1)
원소가 128개 이하이고 값이 64바이트 이하이면 ziplist(7.0+에서는 listpack)를 사용하여 메모리를 절약합니다.
퀴즈 3: DynamoDB에서 GSI(Global Secondary Index)와 LSI(Local Secondary Index)의 차이는?
| 항목 | GSI | LSI |
|---|---|---|
| 파티션 키 | 테이블과 다를 수 있음 | 테이블과 동일 |
| 정렬 키 | 자유롭게 선택 | 자유롭게 선택 |
| 용량 제한 | 없음 | 파티션당 10GB |
| 생성 시점 | 테이블 생성 후에도 가능 | 테이블 생성 시에만 |
| 일관성 | 최종적 일관성만 | 강한 일관성 가능 |
| 프로비저닝 | 별도 RCU/WCU | 테이블 공유 |
실무 팁: 대부분 GSI가 유연하므로 GSI를 우선 사용합니다.
퀴즈 4: MongoDB에서 트랜잭션을 언제 사용하고 언제 피해야 하나요?
사용해야 할 때:
- 여러 문서/컬렉션에 걸친 원자적 업데이트 (결제와 재고 동시 변경)
- 데이터 일관성이 비즈니스적으로 필수
피해야 할 때:
- 단일 문서 업데이트 (MongoDB는 단일 문서 수준에서 이미 원자적)
- 높은 처리량이 필요한 작업 (트랜잭션은 성능 오버헤드 발생)
- 샤딩된 환경에서 빈번한 멀티 샤드 트랜잭션
설계 원칙: 가능한 한 단일 문서에 관련 데이터를 임베딩하여 트랜잭션 필요성을 줄이세요.
퀴즈 5: VACUUM이 PostgreSQL에서 왜 중요한가요?
PostgreSQL의 MVCC는 업데이트/삭제 시 이전 버전을 즉시 제거하지 않습니다. VACUUM은 이 "죽은 튜플(dead tuples)"을 정리합니다.
하지 않으면:
- 테이블 팽창: 디스크 공간 낭비, 순차 스캔 성능 저하
- 인덱스 팽창: 인덱스 크기 증가, 검색 성능 저하
- 트랜잭션 ID 래핑: 약 20억 트랜잭션 후 데이터 손실 위험 (XID wraparound)
Autovacuum이 기본 활성화되어 있지만, 쓰기 부하가 높은 테이블은 Autovacuum 파라미터 튜닝이 필요합니다.
참고 자료
- PostgreSQL 17 공식 문서: https://www.postgresql.org/docs/17/
- MySQL 9.0 공식 문서: https://dev.mysql.com/doc/refman/9.0/en/
- MongoDB 8.0 공식 문서: https://www.mongodb.com/docs/manual/
- Redis 공식 문서: https://redis.io/docs/
- Valkey 프로젝트: https://valkey.io/
- DynamoDB 개발자 가이드: https://docs.aws.amazon.com/dynamodb/
- ScyllaDB 문서: https://opensource.docs.scylladb.com/
- DB-Engines 랭킹: https://db-engines.com/en/ranking
- YCSB 벤치마크: https://github.com/brianfrankcooper/YCSB
- "Designing Data-Intensive Applications" — Martin Kleppmann
이 글에서 다룬 8개 데이터베이스는 각각 고유한 강점을 가지고 있습니다. "최고의 데이터베이스"는 없으며, "최적의 데이터베이스"만 있을 뿐입니다. 워크로드 패턴, 확장 요구사항, 팀 역량, 비용 제약을 종합적으로 고려하여 선택하세요. 그리고 하나의 데이터베이스에 모든 것을 맡기려 하지 말고, Polyglot Persistence 전략으로 각 데이터베이스의 강점을 조합하는 것이 2025년의 모범 사례입니다.