Skip to content
Published on

DB 완전 가이드 — 내부 구조·인덱스·쿼리 플래너·파티셔닝·Vector DB (Season 2 Ep 13, 2025)

Authors

들어가며 — 왜 DB를 깊게 알아야 하는가

"SQL만 쓸 줄 알면 되지 않나?"의 반례:

  • P99 레이턴시 스파이크 → 인덱스·플래너 이해 없이는 디버깅 불가
  • 트랜잭션 격리 이슈 → 격리 수준 모르면 재현·수정 불가
  • 저장 비용 폭증 → 파티셔닝·압축 전략 필요
  • 분산 DB 선택 → 내부 구조 모르면 잘못된 선택
  • Vector 검색 → 임베딩 저장소 설계 필요

2025년 시니어 엔지니어가 DB를 깊게 아는 것은 필수 교양이다.


1부 — 저장 엔진의 두 철학

1.1 B-Tree (Balanced Tree)

읽기 최적화. 전통 RDBMS(PostgreSQL, MySQL InnoDB)의 기본.

     [50]
    /    \
  [20]  [80]
 /  \   /  \
[10][30][70][90]

특징:

  • 균형 이진 트리 (실제로는 fanout 수백)
  • O(log N) 탐색
  • In-place update → 쓰기 amplification 낮음
  • 인덱스 구조로 널리 사용

단점: 랜덤 쓰기가 많으면 분열 + I/O.

1.2 LSM-Tree (Log-Structured Merge)

쓰기 최적화. RocksDB, Cassandra, ScyllaDB, LevelDB, ClickHouse.

Memtable (RAM)
   ↓ flush
L0 SSTables (디스크)
   ↓ compact
L1 SSTables
   ↓ compact
L2 SSTables ...

특징:

  • 모든 쓰기는 append-only → 순차 I/O
  • Compaction이 후속으로 정리
  • 쓰기 속도 매우 빠름
  • 공간·CPU를 compaction에 사용

단점: 읽기가 여러 레이어 탐색 → Bloom Filter 등으로 완화.

1.3 선택 기준

워크로드선호
OLTP (읽기 ~ 쓰기)B-Tree (Postgres, MySQL)
Write-heavy (로그·메시지)LSM (Cassandra, RocksDB)
시계열LSM 또는 컬럼 (InfluxDB, TimescaleDB)
분석컬럼 (ClickHouse, DuckDB)

1.4 2024~2025 트렌드: B-Tree와 LSM의 융합

  • Aurora, Neon: 저장층 분리 (S3 같은 오브젝트 스토리지 + 로컬 캐시)
  • TigerBeetle: 금융 특화, LSM + 결정성
  • FoundationDB: 레이어드, ACID + Key-Value
  • Postgres Incremental Materialized View: 읽기 최적 + 실시간

2부 — 인덱스 심화

2.1 인덱스 종류 8가지

종류용도
B-Tree범위 쿼리 (most common)
Hash동등 조건만
GIN전문 검색, JSONB, Array
GiST지리·도형, 범위 타입
SP-GiST공간 데이터
BRIN거대 테이블의 근사 인덱스
Bloom다중 컬럼 OR 검색
Covering (INCLUDE)인덱스에 추가 컬럼 (Index-only scan)

2.2 인덱스 설계 5원칙

  1. 쿼리가 먼저: 실제 쿼리 보고 설계
  2. 선택도: Selectivity 높은 컬럼 우선
  3. 복합 인덱스 순서: 가장 자주 쓰는 필터부터
  4. INCLUDE로 Covering: 테이블 접근 회피
  5. 사용하지 않는 인덱스는 삭제: 쓰기 성능 저해

2.3 인덱스 안 타는 이유 Top 10

  1. 함수 적용: WHERE LOWER(email) = ... → Expression Index 필요
  2. 암시적 형 변환: WHERE id = '123' (id는 int)
  3. 선행 와일드카드: LIKE '%foo' → Full scan
  4. OR 조건: 옵티마이저가 포기할 수 있음 → UNION
  5. Not Equals (!=): 대부분 인덱스 안 씀
  6. NULL 비교: Postgres는 IS NULL 인덱스 가능, 설계 주의
  7. 데이터가 작음: 플래너가 Sequential Scan 선호
  8. 복합 인덱스 순서 틀림: (a, b) 인덱스에 WHERE b = ?
  9. Statistics 오래됨: ANALYZE 필요
  10. 인덱스 Bloat: REINDEX 필요

3부 — 쿼리 플래너와 EXPLAIN

3.1 플래너의 역할

SQL (선언) → 실행 계획 (절차)로 변환. 비용 기반 최적화 (CBO).

3.2 PostgreSQL EXPLAIN 읽기

EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.country = 'KR'
GROUP BY u.name;

출력 예:

HashAggregate  (cost=1234..5678 rows=100)
  Group Key: u.name
  ->  Hash Join  (cost=500..1200 rows=10000)
        Hash Cond: o.user_id = u.id
        ->  Seq Scan on orders o  (cost=0..800 rows=100000)
        ->  Hash  (cost=300..300 rows=500)
              ->  Index Scan on users_country_idx  (cost=0..300 rows=500)

읽는 법:

  • 안쪽부터 바깥으로
  • cost=startup..total
  • rows: 예상 row 수 (ANALYZE와 실제 비교)
  • actual time: 실제 시간
  • Buffers: 메모리·디스크 접근

3.3 중요 노드 유형

노드의미
Seq Scan전체 스캔 (작은 테이블·선택도 낮을 때 OK)
Index Scan인덱스 사용
Index Only Scan인덱스만으로 해결 (빠름)
Bitmap Heap Scan인덱스로 찾은 후 테이블 일괄
Nested Loop Join작은 outer + 인덱스 있는 inner
Hash Join해시 테이블 만들어 매칭
Merge Join양쪽 정렬된 경우
Hash AggregateGROUP BY
SortORDER BY

3.4 Plan Hint는 Postgres에 없다

MySQL·Oracle은 힌트 있음. Postgres는 ANALYZE + 통계 목표 + random_page_cost 조정으로 유도.


4부 — 트랜잭션 격리

4.1 ACID 재정의

  • Atomicity: 전부 or 무
  • Consistency: 제약 위반 없음
  • Isolation: 동시 실행이 순차 실행처럼
  • Durability: 커밋되면 유지

4.2 격리 수준 4단계 (SQL-92)

수준방지
Read Uncommitted(거의 아무것도 안 방지)
Read CommittedDirty Read
Repeatable Read+ Non-repeatable Read
Serializable+ Phantom

4.3 DB별 기본 격리 수준

DB기본
PostgreSQLRead Committed
MySQL InnoDBRepeatable Read
OracleRead Committed (Serializable 옵션)
SQL ServerRead Committed

4.4 Snapshot Isolation vs Serializable

  • Snapshot Isolation (SI): 각 트랜잭션이 시작 시점 스냅샷을 봄
    • 단점: Write Skew 가능
  • Serializable (MVCC SSI): SI + 충돌 감지 → abort
    • Postgres는 SERIALIZABLE로 SSI 제공

4.5 Write Skew 예

두 의사 중 적어도 한 명 당직 필요:

-- T1: on_call을 읽고 2명 → OK
-- T2: 동시에 on_call 읽고 2명 → OK
-- 둘 다 자기 상태를 off-call로 변경
-- 커밋 → 0명 당직. 제약 위반

Serializable에서만 감지됨.

4.6 Deadlock

둘 이상의 트랜잭션이 서로의 락을 기다림. DB가 감지 → 1개 희생 (abort).

방지:

  • 락 순서 일관 (ID 오름차순)
  • SELECT FOR UPDATE NOWAIT
  • 재시도 로직 (멱등성 + 백오프)

5부 — MVCC (Multi-Version Concurrency Control)

5.1 MVCC 기본

"쓰기는 읽기를 막지 않고, 읽기는 쓰기를 막지 않는다."

각 row의 여러 버전 보관 + 트랜잭션이 자기 스냅샷 버전 읽음.

5.2 Postgres의 MVCC

  • Tuple 헤더: xmin (생성 트랜잭션), xmax (삭제 트랜잭션)
  • Vacuum: 죽은 튜플 정리. 필수.
  • autovacuum: 자동, 하지만 튜닝 필요

5.3 Vacuum Bloat 문제

  • Long-running transaction이 autovacuum 막음
  • Table/Index Bloat → 성능 저하
  • 해결: pg_repack, VACUUM FULL, 주기적 모니터링

5.4 MySQL InnoDB MVCC

  • Undo Log에 이전 버전
  • Purge Thread가 정리
  • Clustered Index (Primary Key로 물리 정렬)

6부 — 파티셔닝과 샤딩

6.1 수직 vs 수평

  • 수직: 컬럼 분할 (거의 안 씀)
  • 수평 (Horizontal): row 분할. Partitioning·Sharding.

6.2 Partitioning (단일 DB 내)

Postgres 네이티브 (10+):

CREATE TABLE events (
  id bigserial,
  event_time timestamptz,
  data jsonb
) PARTITION BY RANGE (event_time);

CREATE TABLE events_2025_01 PARTITION OF events
  FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

장점: 파티션 가지치기, 관리 용이 (DROP old partition). 한계: 단일 DB 내. 단일 서버 용량 제약.

6.3 Sharding (여러 DB)

데이터를 여러 서버에 분산.

샤딩 키 선택:

  • 카디널리티 높음
  • 접근 패턴 고려
  • 리밸런싱 용이
  • Hot Shard 방지

샤딩 방법:

  • Hash: 균등, 범위 쿼리 ❌
  • Range: 범위 쿼리 OK, Hot Shard 위험
  • Directory-based: 유연, 메타데이터 관리

6.4 Cross-shard 쿼리

기본적으로 비쌈. JOIN·ORDER BY가 여러 샤드 걸치면 어플리케이션 레벨 merge.

해결:

  • 같은 샤드에 관련 데이터 (tenant_id로)
  • Read Replica + Materialized View
  • OLAP은 별도 (데이터 웨어하우스)

6.5 분산 SQL DB (샤딩 자동)

  • CockroachDB
  • YugabyteDB
  • Spanner
  • TiDB
  • Vitess (MySQL 위)
  • Citus (Postgres 확장)

7부 — Replication과 HA

7.1 Replication 방식

  • Physical (WAL/Binlog): 바이트 단위, 정확
  • Logical: SQL/Row 단위, 유연 (이기종·부분)

7.2 Postgres Replication 2025

  • Streaming Replication: WAL 전송
  • Logical Replication (10+): 테이블 단위
  • Synchronous 옵션: 확실하지만 느림
  • Patroni: HA 관리 (표준)

7.3 Failover 패턴

Primary 장애 감지
   (consensus: etcd·Patroni)
Standby 중 최신 선택
Primary 승격 + 응용이 DSN 전환
기존 Primary는 Rebuild

핵심: Split-brain 방지 (Fencing).

7.4 Read Replica 활용

  • Read-heavy 워크로드 분산
  • Replication lag 감수
  • "Read-your-writes"는 Primary에서

8부 — PostgreSQL의 2025년 독주

8.1 왜 Postgres가 이겼나

2024~2025년 DB 선택에서 Postgres가 사실상 기본값이 된 이유:

  1. 확장성: 수많은 Extension
  2. JSONB: NoSQL 기능 내장
  3. pgvector: Vector DB 기능
  4. PostGIS: GIS 최강
  5. Timescale: 시계열
  6. Citus: 분산
  7. Logical Replication: CDC·마이그레이션
  8. 라이선스: PostgreSQL License (관대)
  9. 커뮤니티: 안정적, 대기업 종속 없음
  10. 클라우드 관리형: Aurora, RDS, Cloud SQL, Neon, Supabase

8.2 Postgres Extension Top 15 (2025)

Extension용도
pgvectorVector Search
PostGIS지리
TimescaleDB시계열
Citus분산
pg_partman파티션 관리
pg_cron스케줄
pg_stat_statements쿼리 성능
hypopg가상 인덱스
pg_repack온라인 재구성
pg_hint_plan플랜 힌트
pg_trgm유사 검색
unaccent악센트 제거
uuid-osspUUID 생성
pgcrypto암호
hstore키-값

8.3 2024~2025 Postgres 생태계

  • Neon: Serverless Postgres, 브랜치
  • Supabase: Postgres + Auth + Realtime
  • Xata: DevEx 우선
  • Aurora Postgres: AWS 관리형
  • ParadeDB: 검색 확장

9부 — NoSQL: 언제 쓸까

9.1 NoSQL 5 카테고리

카테고리예시용도
DocumentMongoDB, Firestore스키마 유연
Key-ValueRedis, DynamoDB빠른 조회
Wide-columnCassandra, ScyllaDB, HBase대규모 쓰기
GraphNeo4j, Neptune관계 중심
Time-SeriesInfluxDB, Timescale시계열

9.2 2025 현실: "Postgres로 충분"

대부분의 NoSQL 사용 사례를 Postgres가 커버:

  • Document → JSONB
  • Key-Value → UNLOGGED 테이블 또는 Redis 병행
  • Time-Series → TimescaleDB
  • Graph → Apache AGE extension

여전히 NoSQL 선택:

  • 글로벌 저지연 (DynamoDB Global Table)
  • 초대형 쓰기 (Cassandra)
  • 그래프 알고리즘 특화 (Neo4j)

9.3 Redis의 위치

인메모리 캐시·큐·랭킹·Rate Limit. "하나쯤은 다들 갖고 있는" 도구.

2024 라이선스 변경 → Valkey 포크 (AWS·Google 주도). 선택지 늘어남.


10부 — Vector DB

10.1 Vector DB가 필요한 이유

LLM 임베딩 검색·추천 등을 위한 근사 최근접 이웃 (ANN) 검색.

10.2 ANN 알고리즘

  • HNSW (Hierarchical Navigable Small World): 빠름·정확, 메모리 ↑
  • IVF (Inverted File): 클러스터링 기반
  • PQ (Product Quantization): 메모리 절감
  • HNSW + PQ: 하이브리드

10.3 2025 Vector DB 비교

DB특징
pgvectorPostgres 확장, 통합 최고
QdrantRust, 빠름, 필터링 강함
Weaviate하이브리드 검색, GraphQL API
Milvus대규모, Zilliz
PineconeSaaS, 관리 편함
LanceDB파일 기반, 임베디드
TurbopufferServerless

10.4 2025 추천

  • 간단한 RAG: pgvector (기존 Postgres에 추가)
  • 대규모·고급 필터: Qdrant 또는 Weaviate
  • 완전 관리형: Pinecone
  • 로컬/엣지: LanceDB

10.5 pgvector 예시

CREATE EXTENSION vector;

CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  content text,
  embedding vector(1536)
);

CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);

-- 검색
SELECT content
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

11부 — DB 로드맵 6개월

Month 1: SQL 심화

  • Window Function, CTE, Recursive
  • 트랜잭션 격리·Deadlock 실습
  • 인덱스 설계

Month 2: Postgres 내부

  • EXPLAIN 완전 이해
  • MVCC·Vacuum
  • WAL·Replication

Month 3: Storage Engine

  • DDIA 3장 (Storage)
  • B-Tree / LSM 구현 체험
  • RocksDB·Cassandra 학습

Month 4: 분산 DB

  • CockroachDB·Spanner 구조
  • Sharding 설계
  • Replication 운영

Month 5: Vector DB

  • pgvector 실전
  • HNSW 이해
  • RAG 데이터 레이어 구축

Month 6: 최적화·운영

  • Slow Query 분석
  • 인덱스 튜닝
  • Connection Pooling (PgBouncer)
  • Schema Migration 무중단 (Postgres 심화)

12부 — DB 체크리스트 12

  1. B-Tree vs LSM-Tree 선택 기준을 안다
  2. Postgres 인덱스 타입 5가지를 용도별로 안다
  3. 인덱스 안 타는 이유 5가지를 말할 수 있다
  4. EXPLAIN ANALYZE 출력을 읽을 수 있다
  5. 트랜잭션 격리 4단계를 방지 현상과 함께 안다
  6. Write Skew를 설명할 수 있다
  7. MVCC와 Vacuum의 관계를 안다
  8. Partitioning vs Sharding 차이를 안다
  9. Hash vs Range 샤딩 트레이드오프를 안다
  10. Postgres Extension Top 5를 안다
  11. pgvector와 Qdrant 선택 기준을 안다
  12. Connection Pool (PgBouncer) 필요성을 안다

13부 — DB 안티패턴 10

  1. ORM만 믿고 쿼리 점검 없음: N+1, 과다 컬럼 로드 흔함
  2. 인덱스 하나씩 무제한 추가: 쓰기 성능 저하. 사용 안 되는 거 삭제
  3. Long transaction: MVCC bloat. 트랜잭션 짧게
  4. 기본 격리 수준 맹신: Read Committed 한계 모르고 Write Skew 버그
  5. Connection 하나씩 관리: Pool 없이 수천 connection → Postgres 터짐
  6. JSONB에 모든 걸: 스키마 강점 포기. 정형은 컬럼으로
  7. 샤딩 키 잘못: Hot Shard → 재샤딩 지옥
  8. Migration 무계획 downtime: 무중단 마이그레이션 전략 필수
  9. Vacuum 튜닝 무시: 저도 모르게 테이블 bloat 10배
  10. DB를 메시지 큐로: 가능하지만 비추. Redis/Kafka 사용

마치며 — DB는 "프로덕션 시스템의 중력"이다

애플리케이션은 바꿔도, DB 스키마는 쉽게 못 바꾼다. DB는 프로덕션 시스템에서 가장 관성이 큰 부분.

그래서 처음에 잘 설계해야 한다:

  • 인덱스 설계로 5년 버틸 구조
  • 파티션·샤딩으로 10배 성장 대비
  • 격리 수준으로 데이터 무결성 보장
  • 관측성·백업으로 재앙 대비

2025년의 DB 엔지니어링은:

  • Postgres First: 대부분은 이것으로 해결
  • Vector DB: LLM 시대 필수
  • 분산 DB: 초대형 서비스에만
  • NoSQL: 명확한 이유 있을 때만

다음 글 예고 — "네트워크 완전 가이드: HTTP/3·QUIC·TLS 1.3·gRPC·WebSocket·CDN"

Season 2 Ep 14는 인터넷의 배관, 네트워크. 다음 글은:

  • TCP vs UDP vs QUIC
  • HTTP/1.1 → 2 → 3 진화
  • TLS 1.3 Handshake
  • gRPC vs REST vs GraphQL vs tRPC
  • WebSocket vs SSE vs Long Polling
  • CDN 아키텍처 (Cloudflare, Fastly)

보이지 않는 전선, 다음 글에서.