MVCC의 정의
MVCC(Multi-Version Concurrency Control)는 ‘다중 버전 동시성 제어’라는 뜻으로, 데이터베이스가 하나의 데이터에 대해 여러 버전을 동시에 유지하면서 동시성을 처리하는 기법입니다. PostgreSQL, MySQL의 InnoDB, Oracle 등 대부분의 상용 DBMS가 이 방식을 채택하고 있습니다.
전통적인 락(lock) 기반 동시성 제어는 한 트랜잭션이 데이터를 읽거나 쓸 때 다른 트랜잭션이 접근하지 못하도록 잠그는 방식입니다. 하지만 이 방식은 읽기 작업조차 쓰기 작업을 기다리게 만들어 성능 저하를 유발합니다. MVCC는 ‘읽기 작업은 락을 걸지 않고, 대신 데이터의 스냅샷(특정 시점의 버전)을 읽는다’는 아이디어로 이 문제를 해결합니다.
동작 원리와 예시
MVCC의 핵심은 데이터를 수정할 때 기존 데이터를 바로 덮어쓰지 않고, 새로운 버전을 추가로 만든다는 점입니다. 각 버전에는 생성된 시점(트랜잭션 ID)이 기록되고, 트랜잭션은 자신이 시작된 시점을 기준으로 ‘보이는 버전’만 읽습니다.
도서관에 비유하면 이해하기 쉽습니다. 어떤 책의 최신 개정판이 나와도, 이미 대출해서 읽고 있는 사람에게는 예전 판본이 그대로 유지되는 것과 같습니다. 새 독자는 최신판을, 기존 독자는 자신이 빌린 시점의 판본을 계속 읽을 수 있는 것이죠.
- 트랜잭션 A가 특정 로우를 읽기 시작함 (버전 1 확인)
- 트랜잭션 B가 같은 로우를 수정 → 버전 2 생성 (커밋 전까지는 비공개)
- 트랜잭션 A는 계속 버전 1을 읽음 (일관된 스냅샷 유지)
- B가 커밋하면 이후 시작되는 트랜잭션들은 버전 2를 봄
이렇게 여러 버전이 쌓이기 때문에, 더 이상 참조되지 않는 오래된 버전은 주기적으로 정리해야 합니다. PostgreSQL의 VACUUM이나 InnoDB의 언두 로그(undo log) 정리 작업이 바로 이 역할을 합니다.
실무에서 MVCC를 쓰는 이유
MVCC를 사용하면 읽기와 쓰기가 서로를 막지 않기 때문에 동시성이 크게 향상됩니다. 예를 들어 수많은 사용자가 동시에 게시글을 조회하는 서비스에서, 누군가 글을 수정 중이라고 해서 조회 요청이 대기 상태에 빠지지 않습니다.
또한 MVCC는 트랜잭션 격리 수준(isolation level)을 구현하는 기반이 되기도 합니다. ‘REPEATABLE READ’나 ‘READ COMMITTED’ 같은 격리 수준은 결국 ‘어떤 버전을 보여줄 것인가’를 결정하는 규칙이라고 볼 수 있습니다. 다만 MVCC가 만능은 아닙니다. 버전이 계속 쌓이면 저장 공간과 정리 비용이 늘어나므로, 트랜잭션을 너무 오래 유지하면 오히려 성능 문제를 일으킬 수 있다는 점도 함께 알아두어야 합니다.