데이터베이스 엔지니어링 마스터 노트 시리즈 8편 (마지막). Connection Pooling이 처리량을 결정하는 이유, 분산 트랜잭션 2PC vs Saga, Eventual Consistency가 표준이 된 이유, CAP·PACELC 정리의 실제 의미, Cache Invalidation 5가지 패턴, Materialized View 활용까지 — 시리즈 마무리.
이 글은 데이터베이스 엔지니어링 마스터 노트 시리즈의 마지막 여덟 번째 편입니다. 1~7편이 토대였다면, 이번엔 운영·분산 시스템에서 마주치는 고급 주제 모음.
CAP·PACELC가 분산 DB 선택을 결정. Connection Pool 잘못 잡으면 처리량 폭락. Saga 없이 마이크로서비스 트랜잭션 X. Cache Invalidation은 영원한 문제.
Connection Pooling
문제 — 연결은 비싸다
새 PostgreSQL 연결:
- TCP handshake
- SSL 협상
- 인증
- Backend process fork
→ 50 ~ 수백 ms
PostgreSQL 연결당 RAM ~10MB
수천 동시 연결 = 수십 GB RAM + 컨텍스트 스위칭 폭발.
해결 — Connection Pool
App ← (재사용) ← Pool ← (소수 영구) ← DB
App이 connection을 반납·재사용. DB는 적은 수의 영구 연결만 유지.
도구:
- PgBouncer (PostgreSQL) — Transaction Pooling
- ProxySQL (MySQL)
- HikariCP (Java) — 애플리케이션 사이드 풀
- AWS RDS Proxy
Pool 모드 (PgBouncer)
| 모드 | 의미 |
|---|---|
| Session | 클라이언트 연결 종료 시까지 점유 |
| Transaction | 트랜잭션 끝나면 반납 (가장 일반적) |
| Statement | 매 쿼리 후 반납 (트랜잭션 X) |
여기서 정말 중요한 시험 함정 — Transaction Pooling 사용 시 prepared statement·session 변수 주의. Session에 묶인 기능 못 씀. 코드에서 바인딩 사용.
분산 트랜잭션
2PC — Two-Phase Commit
Coordinator → 참여 노드 모두에 PREPARE
각 노드: 트랜잭션 준비 + 응답
Coordinator → 모두 OK면 COMMIT, 하나라도 실패면 ROLLBACK
각 노드: 결정 적용
문제:
- Coordinator 장애 = 무한 대기 (블로킹)
- 느림 (모든 노드 대기)
- 운영 복잡
여기서 시험 함정이 하나 있어요. 2PC는 사실상 마이크로서비스에서 거의 안 씀. 이론적·과거 표준이지만, 모던 분산 시스템은 Saga 선호.
Saga Pattern
각 단계를 독립 트랜잭션으로 분해 + 보상 트랜잭션 정의.
주문 처리 Saga:
1. 주문 생성 → (실패 시 X)
2. 결제 → (실패 시 보상: 주문 취소)
3. 재고 차감 → (실패 시 보상: 결제 환불, 주문 취소)
4. 배송 → (실패 시 보상: 재고 복구, 환불, 주문 취소)
종류:
- Choreography — 각 서비스가 이벤트 발행·구독 (분산)
- Orchestration — 중앙 코디네이터가 흐름 제어 (집중)
여기서 정말 중요한 시험 함정 — Saga는 ACID 보장 X, BASE만. 실시간으론 일관성 깨진 상태 보일 수 있음. 보상 가능 비즈니스에만 적용 (송금·주문 OK / 의료 처방 X).
Eventual Consistency vs Strong Consistency
Strong Consistency
쓰기 → 즉시 모든 노드 반영
모든 읽기 = 최신 값
비싼 비용. 동기 복제·합의 알고리즘 필요.
Eventual Consistency
쓰기 → 일부 노드만 즉시 반영
시간 지나면 모두 반영
중간에 stale 읽기 가능
저렴. 비동기 복제. 분산 시스템 표준.
사용 사례
| 영역 | 일관성 |
|---|---|
| 금융 잔액 | Strong |
| **재고 (정확) | Strong (또는 보상 가능 Saga) |
| 타임라인·게시물 수 | Eventual |
| 분석·대시보드 | Eventual |
CAP 정리
분산 시스템은 다음 3가지 중 2개만 보장 가능.
C - Consistency (모든 노드 같은 값)
A - Availability (요청 항상 응답)
P - Partition Tolerance (네트워크 단절 견딤)
P는 현실에서 불가피 → 실제 선택은 CP vs AP.
| 시스템 | 분류 |
|---|---|
| RDBMS (단일 노드) | CA (분산 X) |
| HBase·MongoDB(단일 primary) | CP |
| Cassandra·DynamoDB·Riak | AP |
| Spanner·CockroachDB | CP (특수) |
여기서 시험 함정이 하나 있어요. CAP은 "장애 시" 트레이드오프. 정상 시는 다 됨. 네트워크 분할 발생 시 C 또는 A 중 하나 포기.
PACELC — CAP 보충
P (Partition) → A or C 선택
E (Else, 정상 시) → L (Latency) or C (Consistency) 선택
CAP은 장애만 다룸. PACELC = 정상 시까지.
| 시스템 | PACELC |
|---|---|
| Cassandra | PA / EL (둘 다 가용성·지연 우선) |
| MongoDB | PA / EC (가용성·일관성) |
| HBase | PC / EC (둘 다 일관성) |
| DynamoDB | PA / EL |
여기서 정말 중요한 시험 함정 — PACELC가 CAP보다 실무 의사 결정에 유용. 정상 시 latency vs consistency 선택이 더 자주 부딪힘.
Cache Invalidation — 영원한 문제
"There are only two hard things in Computer Science: cache invalidation and naming things." — Phil Karlton
패턴 1 — Cache-Aside (Lazy Loading)
def get_user(id):
user = cache.get(f"user:{id}")
if user:
return user
user = db.query("SELECT * FROM users WHERE id = ?", id)
cache.set(f"user:{id}", user, ttl=300)
return user
def update_user(id, ...):
db.update(...)
cache.delete(f"user:{id}") # 무효화
가장 일반적. 단순.
패턴 2 — Write-Through
쓰기 시 cache·DB 동시 업데이트.
def update_user(id, ...):
cache.set(f"user:{id}", new_value)
db.update(...)
장점 — cache 항상 최신. 단점 — 쓰기 느림.
패턴 3 — Write-Behind (Write-Back)
쓰기는 cache만, DB는 비동기.
def update_user(id, ...):
cache.set(f"user:{id}", new_value)
queue.publish(("update_user", id, ...)) # 비동기 DB 반영
장점 — 쓰기 매우 빠름. 단점 — 데이터 손실 위험 (cache 다운).
패턴 4 — TTL 만료
단순히 시간 후 자동 삭제. 일관성 ↓ but 단순.
패턴 5 — Pub/Sub Invalidation
DB 변경 시 모든 cache 노드에 무효화 메시지.
여기서 시험 함정이 하나 있어요. Cache Aside + DB 업데이트 후 cache 삭제 순서. 잘못된 순서는 race 발생. 표준 — DB 먼저 commit → cache delete (set X). delete가 race에 안전.
Materialized View
뷰의 결과를 물리적으로 저장.
-- PostgreSQL
CREATE MATERIALIZED VIEW monthly_sales AS
SELECT date_trunc('month', order_date) AS month,
SUM(amount) AS total
FROM orders
GROUP BY 1;
-- 새로고침
REFRESH MATERIALIZED VIEW monthly_sales;
-- 동시 조회 가능 (CONCURRENTLY)
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_sales;
장점 — 무거운 집계 미리 계산, 조회 즉시. 단점 — 신선도 ↓, 새로고침 비용.
용도 — 대시보드·통계·자주 안 변하는 집계.
Database Federation
여러 DB를 논리적으로 하나처럼.
Application
↓
Federation Layer
├── DB A (Users)
├── DB B (Products)
└── DB C (Orders)
장점 — 도메인별 분리, 독립 확장. 단점 — Cross-DB JOIN 어려움.
도구 — PostgreSQL FDW (Foreign Data Wrapper), Presto, Trino.
Read/Write Pattern 결정
| 사용 사례 | 패턴 |
|---|---|
| 읽기 매우 많음 | Read Replica + 캐시 |
| 쓰기 매우 많음 | 샤딩 + 비동기 처리 |
| 분석 쿼리 | Column Store + ETL |
| 실시간 분석 | OLAP DB (ClickHouse) |
| 엄격 일관성 | 단일 노드 + Read Replica |
| 글로벌 분산 | Multi-region (AP) |
CQRS — Command Query Responsibility Segregation
쓰기 모델과 읽기 모델 분리.
Command (쓰기): 정규화된 OLTP DB
↓ 이벤트 발행
Query (읽기): denormalized OLAP / 검색 인덱스 / 캐시
장점 — 각 모델 최적화. 단점 — 복잡, eventual consistency.
용도 — 큰 시스템, 다양한 조회 패턴.
Event Sourcing
상태 대신 이벤트의 시퀀스 저장.
전통: account.balance = 100 (현재 값만)
Event Sourcing:
- AccountCreated (initial 0)
- Deposited 100
- Withdrawn 30
- Deposited 30
→ 현재 값 = 이벤트 재생 결과 100
장점 — 모든 변경 기록, 시간 여행 가능, 감사 자연스러움. 단점 — 스토리지 ↑, 쿼리 복잡 (CQRS 흔히 결합).
시리즈 마무리 — 8편 종합
1편부터 8편까지의 핵심:
| 편 | 주제 | 한 줄 |
|---|---|---|
| 1 | ACID | 트랜잭션의 4 보장. Isolation Level의 Phantom·Repeatable 차이 |
| 2 | Indexing | B+Tree·EXPLAIN·Composite Leftmost·Bloom Filter |
| 3 | Partitioning | Range·List·Hash·Pruning·UNIQUE 제약 |
| 4 | Sharding | Consistent Hashing·Cross-Shard JOIN·샤딩 키 신중 선정 |
| 5 | Replication | 동기 vs 비동기·Lag·Failover·Split-Brain |
| 6 | Concurrency | Locking·MVCC·Optimistic·Deadlock |
| 7 | Internals | Page·WAL·Buffer Pool·Row vs Column |
| 8 | Advanced | Pool·2PC vs Saga·CAP·PACELC·Cache·CQRS |
DB 엔지니어링은 트레이드오프 인식의 학문. 한 가지 정답 X — 워크로드·일관성 요구·확장성 요구에 따라 선택.
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 8편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- Connection Pool = 연결 재사용, 처리량의 핵심
- 도구 — PgBouncer·ProxySQL·HikariCP·RDS Proxy
- Pool 모드 — Session / Transaction(보통) / Statement
- Transaction Pool 시 prepared statement·session 변수 주의
- 2PC = 동기 분산 트랜잭션, Coordinator 장애 무한 대기
- 모던 = 거의 안 씀
- Saga = 단계 + 보상 트랜잭션, ACID X, BASE
- Choreography(이벤트) vs Orchestration(중앙)
- Strong vs Eventual Consistency
- 금융·재고 = Strong / 타임라인·통계 = Eventual
- CAP = 분산 시 C·A·P 중 2개 (P 불가피, 실제 CP vs AP)
- CP — HBase·MongoDB(primary) / AP — Cassandra·DynamoDB
- PACELC = 장애 시 + 정상 시 트레이드오프
- 정상 시 Latency vs Consistency
- Cache Invalidation 5 패턴 — Cache-Aside / Write-Through / Write-Behind / TTL / Pub/Sub
- Cache-Aside = 가장 일반적, 표준
- DB commit 후 cache delete (set X, race 안전)
- Materialized View = 결과 물리 저장, REFRESH 필요
- 무거운 집계 미리 계산
- Database Federation = 여러 DB 하나처럼, FDW·Presto
- Read/Write 패턴 — Read 많음=Replica·캐시 / Write 많음=샤딩
- CQRS = Command/Query 모델 분리
- 정규화 OLTP + denormalized OLAP
- Event Sourcing = 상태 X, 이벤트 시퀀스
- CQRS와 흔히 결합
- 감사·시간 여행 자연스러움
시리즈 다른 편 (시리즈 마지막)
- 1편 — ACID·트랜잭션
- 2편 — 인덱싱
- 3편 — 파티셔닝
- 4편 — 샤딩
- 5편 — 복제
- 6편 — 동시성 제어
- 7편 — 내부 구조
- 8편 — 고급 주제 (현재 글)
공식 문서: Martin Kleppmann의 Designing Data-Intensive Applications — DB 엔지니어링의 깊이를 다지는 정평 있는 도서.
데이터베이스 엔지니어링 시리즈는 여기서 마무리. 1편부터 8편까지의 흐름이 머리에 남으면 시스템 설계·면접·운영 모두에서 든든한 토대가 됩니다.