데이터베이스 용어 사전
동시성 제어다중 버전 동시성 제어 · 스냅샷

MVCC

한 행의 여러 버전을 두고 읽기와 쓰기가 서로 다른 버전을 보게 하는 방식. 읽기가 쓰기를 막지 않는다.

다중 버전 동시성 제어(Multi-Version Concurrency Control). 핵심 약속은 한 문장이다.

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

어떻게 가능한가 — 버전을 나눈다

  • UPDATE 는 기존 행을 덮어쓰지 않는다

    • 새 버전을 만들고, 옛 버전은 잠시 남긴다
  • 읽는 트랜잭션 — → 자기 시작 시점의 '스냅샷'에 맞는 옛 버전을 본다

  • 쓰는 트랜잭션 — → 새 버전을 만든다

    • 서로 기다릴 일이 없다

은행 명세서에 비유하면 — 내가 잔액을 읽기 시작한 순간의 사진을 보고, 그 사이 다른 사람이 입금해 잔액이 바뀌어도 내 사진은 안 흔들린다.

락(2PL)만 쓰던 시절의 한계

순수 2PL 에서는

  • 읽기(S락)가 쓰기(X락)를 막고, 쓰기가 읽기를 막는다
  • 보고서를 만드느라 큰 테이블을 읽는 트랜잭션이
    • 그 테이블 전체의 쓰기를 막아 버린다

MVCC가 이 벽을 깼다. 현대 DB는 읽기는 MVCC 스냅샷(락 없이), 쓰기는 락으로 분업한다.

두 엔진의 구현이 다르다

  • [PostgreSQL] — 버전을 테이블(힙) 안에 직접 쌓는다

    • 각 튜플에 xmin(만든 트랜잭션 ID) · xmax(지운 트랜잭션 ID)
    • UPDATE = 옛 튜플을 '죽은 튜플'로 표시 + 새 튜플 추가
    • 죽은 튜플이 쌓이면 테이블이 부푼다(bloat) → VACUUM 이 청소
  • [MySQL InnoDB] — 버전을 undo log 로 관리한다

    • 각 행에 숨은 칼럼 DB_TRX_ID · DB_ROLL_PTR
    • 옛 버전이 필요하면 undo log 를 거꾸로 따라가 재구성
    • 더는 필요 없는 undo 는 purge 스레드가 정리

공통의 독 — 긴 트랜잭션

아주 오래 열린 트랜잭션이 하나 있으면
  → "그 트랜잭션이 아직 볼 수도 있는" 옛 버전을 지울 수 없다
  → PostgreSQL: 테이블·인덱스가 계속 부푼다
  → InnoDB:     undo 히스토리가 길어져 조회가 느려진다

구현은 달라도 증상은 판박이다. "트랜잭션은 짧게"가 성능 원칙이 아니라 구조적 요구인 이유다.

면접 함정

  • "MVCC면 락이 필요 없다" → 읽기만 그렇다. 쓰기끼리는 여전히 락으로 충돌한다.
  • "MVCC면 Lost Update도 막힌다" → 아니다. 별도로 막아야 한다.

버전을 직접 들여다보기

-- PostgreSQL: 숨은 시스템 칼럼으로 버전 정보를 본다
SELECT xmin, xmax, ctid, * FROM account WHERE id = 'A';
-- xmin: 이 버전을 만든 트랜잭션 ID
-- xmax: 이 버전을 지운/갱신한 트랜잭션 ID (0이면 아직 유효)
-- ctid: (페이지번호, 페이지 내 위치) = 물리 위치

SELECT txid_current();     -- 지금 내 트랜잭션 ID

UPDATE를 한 번 하고 다시 조회해 보면 ctid가 바뀌어 있다 — 같은 자리를 고친 게 아니라 새 튜플이 추가됐다는 증거다.

-- MySQL: 현재 열린 트랜잭션과 undo 히스토리
SELECT trx_id, trx_started, trx_state FROM information_schema.innodb_trx;
SHOW ENGINE INNODB STATUS\G      -- History list length

읽기 일관성의 단위가 다르다

  • READ COMMITTED — 문장(statement) 마다 새 스냅샷

    • 같은 트랜잭션 안에서도 문장마다 다른 값을 볼 수 있다
  • REPEATABLE READ — 트랜잭션 시작 시점 스냅샷을 끝까지 유지

    • 같은 트랜잭션 안에서는 언제 읽어도 같은 값

이 한 줄이 "왜 리포트의 합계와 건수가 안 맞는가" 를 설명한다. 여러 번 조회해 하나의 결과를 만드는 작업은 REPEATABLE READ로 묶어야 한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 동시성 제어 — 2PL·MVCC·락·데드락(PostgreSQL·MySQL)