백엔드 데이터 인프라 37편 — MVCC 소개

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 37편. MVCC 의 큰 그림 — 읽는 사람과 쓰는 사람이 서로 막지 않는 PG 핵심 메커니즘 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 37편 — MVCC 소개

이 글은 백엔드 데이터 인프라 시리즈 70편 중 37편이에요. 17편 트랜잭션 ·24편 DML 개요 에서 잠깐 본 MVCC 의 큰 그림.

MVCC 란

MVCC = Multi-Version Concurrency Control. "같은 행의 여러 버전을 동시에 유지" 해 — 읽기·쓰기 충돌을 락 없이 해결.

핵심 원칙: 읽는 사람은 쓰는 사람을 막지 않고, 쓰는 사람은 읽는 사람을 막지 않는다.

-- 트랜잭션 A (쓰는 사람)
BEGIN;
UPDATE users SET name = 'New' WHERE id = 1;

-- 트랜잭션 B (읽는 사람, 동시)
SELECT name FROM users WHERE id = 1;
-- 'Old' 가 보임 — A 의 변경은 아직 안 보임 (A 가 COMMIT 안 함)

-- 트랜잭션 A COMMIT
COMMIT;

-- B 가 같은 SELECT 다시
SELECT name FROM users WHERE id = 1;
-- 'New' 보임 (격리 수준에 따라 — READ COMMITTED 면)

MySQL 의 InnoDB·Oracle 도 비슷한 MVCC. SQL Server 도 옵션으로. DB 의 동시성 핵심 도구.

락 기반 vs MVCC

[락 기반 (옛 RDBMS)]
WRITE                      READ
  ↓                          ↓
[테이블 락]              [대기]
  ↓                          ↓
완료              ←──   읽기 가능

→ READ 가 WRITE 끝까지 대기. 시스템 정지.
[MVCC]
WRITE              READ (동시)
  ↓                  ↓
[새 버전 생성]    [옛 버전 읽기]
  ↓                  ↓
완료              완료

→ 서로 안 막음. 시스템 흐름 유지.

MVCC 의 혁명. PG·MySQL InnoDB·Oracle 모두 비슷한 원리.

xmin·xmax — 행 버전 식별

PG 각 행은 숨겨진 시스템 컬럼을 가짐.

컬럼 의미
xmin 이 행을 만든 트랜잭션 ID
xmax 이 행을 무효화한 트랜잭션 ID (살아있으면 0)
ctid 물리 위치
SELECT xmin, xmax, ctid, * FROM users WHERE id = 1;

 xmin | xmax | ctid  | id | name
------+------+-------+----+-------
 5000 |    0 | (1,3) |  1 | Alice

xmin=5000 = 트랜잭션 5000이 이 행 만듦. xmax=0 = 아직 무효화 X.

UPDATE 가 만드는 두 버전

UPDATE users SET name = 'Bob' WHERE id = 1;   -- 트랜잭션 5001
SELECT xmin, xmax, * FROM users WHERE id = 1;

 xmin | xmax | name
------+------+-------
 5001 |    0 | Bob       ← 새 버전 (살아있음)

-- 옛 버전 (트랜잭션 5001이 무효화)
 xmin | xmax | name
------+------+-------
 5000 | 5001 | Alice    ← (다른 트랜잭션 시점에서만 보임)

같은 id=1 행이 두 버전. 각 트랜잭션은 "자기 시작 시점" 의 버전 봄.

스냅샷 — 트랜잭션의 시점

각 트랜잭션 = "자기만의 데이터베이스 스냅샷". 시작 시점에 "이 트랜잭션 ID 까지의 커밋된 행" 만 보임.

시간: ──→
트랜잭션 100 시작: 봅니다 트랜잭션 1~99 의 커밋된 행
트랜잭션 200 시작: 봅니다 트랜잭션 1~199 의 커밋된 행
트랜잭션 300 시작: 봅니다 1~299 의 커밋된 행

이게 PG MVCC 의 핵심.

락 vs MVCC 의 격리

17편 의 격리 수준 4개:

수준 구현
READ UNCOMMITTED PG 지원 X (READ COMMITTED 처리)
READ COMMITTED (기본) 각 SELECT 마다 새 스냅샷
REPEATABLE READ 트랜잭션 시작 시점 스냅샷 일관
SERIALIZABLE + 직렬화 충돌 검출

PG 의 모든 격리 = 락 없이 MVCC 로. 다른 DB(MySQL 등) 와 차이.

VACUUM 의 필요성

MVCC 의 부산물 — "옛 버전 행들이 누적". 27편 DELETE 깊이 에서 다룬 bloat.

INSERT  → 행 1개
UPDATE  → 옛 버전 표시 + 새 버전 (행 2개)
DELETE  → 표시만 (행 1개 여전히 디스크)

→ 시간 지나면 디스크 부풀음

autovacuum 이 옛 버전 회수. PG 운영의 핵심.

트랜잭션 ID 의 한계 — Wraparound

PG 트랜잭션 ID = 32bit (~40억). 40억 까지 도달하면 — 처음으로 돌아옴 (Wraparound).

autovacuum 의 두 번째 역할 — Wraparound 방지. 옛 트랜잭션 ID 의 행을 "frozen" 표시 → 영구.

PG 운영의 큰 위험 = Transaction ID Wraparound — autovacuum 이 못 따라가면 데이터베이스 강제 종료 가능. 운영 모니터링 필수.

MVCC 와 ACID

7편 관계형 모델 의 ACID:

  • A 원자성 — 트랜잭션 묶음
  • C 일관성 — 제약 조건
  • I 격리성 — MVCC 가 핵심
  • D 영속성 — WAL

MVCC 가 "I (격리성)" 의 PG 구현 방식. 락이 거의 없어 — "높은 동시성" + "일관된 읽기".

MVCC 의 장단점

장점 단점
읽기·쓰기 안 막음 디스크 비용 (옛 버전)
높은 동시성 autovacuum 필수
일관된 스냅샷 Wraparound 위험
락 거의 없음 bloat 모니터링

장점이 압도적. PG 가 "고동시성 OLTP" 에 강한 이유.

함정 5가지

(1) MVCC 의 동작 모름

UPDATE = 새 행 추가·옛 표시. INSERT 만 단순. 모르면 운영 디버깅 불가.

(2) autovacuum 비활성화

옛 버전 누적 → bloat 폭증. 절대 비활성화 X.

(3) 큰 트랜잭션 오래 열어 둠

BEGIN;
-- ... 1시간 작업
COMMIT;

그 1시간 동안 — 다른 트랜잭션이 만든 옛 버전들이 회수 안 됨. 큰 bloat.

(4) Wraparound 모니터링 X

SELECT datname, age(datfrozenxid) FROM pg_database; — 큰 값이면 위험.

(5) MVCC = 락 없음 오해

대부분의 SELECT·UPDATE 는 락 없음. 단 — SELECT FOR UPDATE·DDL·EXCLUSIVE LOCK 등은 락. 38편 격리 수준에서 깊이.

🎯 MVCC 5단계

(1) 각 트랜잭션 = 스냅샷. (2) UPDATE = 새 버전 + 옛 표시. (3) 읽기·쓰기 안 막음. (4) autovacuum 이 회수. (5) Wraparound 모니터링. PG 운영의 토대.

한 줄 정리 — MVCC = PG 동시성 핵심. 각 트랜잭션 자기 스냅샷. xmin·xmax 로 버전 관리. autovacuum 으로 옛 버전 회수. Wraparound 모니터링 필수. 락 거의 없는 높은 동시성이 PG 강점.

시험 직전 한 번 더 — MVCC 입문자가 매번 헷갈리는 것

  • MVCC = 다중 버전 동시성 제어
  • 읽기·쓰기 서로 안 막음
  • xmin = 만든 트랜잭션
  • xmax = 무효화 트랜잭션 (0 = 살아있음)
  • ctid = 물리 위치
  • UPDATE = 새 행 + 옛 표시
  • 트랜잭션 = 자기만의 스냅샷
  • READ COMMITTED = 각 SELECT 새 스냅샷
  • REPEATABLE READ = 시작 시점 일관
  • SERIALIZABLE = + 직렬화 충돌 검출
  • autovacuum 필수 (옛 버전 회수)
  • bloat = 옛 버전 누적
  • 큰 트랜잭션 = bloat 폭증
  • Wraparound = 트랜잭션 ID 40억 한계
  • autovacuum 이 frozen 표시
  • pg_database.datfrozenxid 모니터
  • MVCC = ACID 의 I (격리성)
  • 락 vs MVCC = 옛 RDBMS vs 모던
  • MySQL InnoDB·Oracle 도 비슷
  • SELECT FOR UPDATE = 명시적 락
  • DDL = EXCLUSIVE 락
  • 운영 = autovacuum 튜닝 + bloat 모니터링

시리즈 다른 편

시리즈 다음 글

다음 글(38편)에서는 MVCC 격리 수준 깊이 — READ COMMITTED·REPEATABLE READ·SERIALIZABLE 차이.

공식 문서: PostgreSQL 18 — MVCC에서 더 자세한 사양을 확인할 수 있어요.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!