LabHub

블로그

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

한국어English日本語中文

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

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

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


1부 — 저장 엔진의 두 철학

1.1 B-Tree (Balanced Tree)

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

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

특징:

단점: 랜덤 쓰기가 많으면 분열 + 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 ...

특징:

단점: 읽기가 여러 레이어 탐색 → 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의 융합


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)

읽는 법:

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 재정의

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

4.5 Write Skew 예

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

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

Serializable에서만 감지됨.

4.6 Deadlock

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

방지:


5부 — MVCC (Multi-Version Concurrency Control)

5.1 MVCC 기본

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

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

5.2 Postgres의 MVCC

5.3 Vacuum Bloat 문제

5.4 MySQL InnoDB MVCC


6부 — 파티셔닝과 샤딩

6.1 수직 vs 수평

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)

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

샤딩 키 선택:

샤딩 방법:

6.4 Cross-shard 쿼리

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

해결:

6.5 분산 SQL DB (샤딩 자동)


7부 — Replication과 HA

7.1 Replication 방식

7.2 Postgres Replication 2025

7.3 Failover 패턴

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

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

7.4 Read Replica 활용


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 생태계


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가 커버:

여전히 NoSQL 선택:

9.3 Redis의 위치

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

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


10부 — Vector DB

10.1 Vector DB가 필요한 이유

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

10.2 ANN 알고리즘

10.3 2025 Vector DB 비교

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

10.4 2025 추천

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 심화

Month 2: Postgres 내부

Month 3: Storage Engine

Month 4: 분산 DB

Month 5: Vector DB

Month 6: 최적화·운영


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는 프로덕션 시스템에서 가장 관성이 큰 부분.

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

2025년의 DB 엔지니어링은:


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

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

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

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다