https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html
이 글에서는 위 문서를 요약하여 MVCC가 무엇인지, PostgreSQL이 이를 어떻게 구현하는지, 그리고 왜 그것이 문제인지에 대해 다룹니다.
Multi-Version Concurrency Control (MVCC)란 무엇인가?
DBMS에서 MVCC의 목표는 여러 쿼리가 동시에 데이터베이스를 읽고 쓸 수 있도록 하여 가능한 한 서로 간섭하지 않도록 하는 것입니다.
MVCC의 기본 아이디어는 DBMS가 기존 행을 절대 덮어쓰지 않는다는 것입니다. 대신, 각 논리적 행에 대해 DBMS는 여러 물리적 버전을 유지합니다. 애플리케이션이 쿼리를 실행할 때, DBMS는 특정 버전 순서(예: 생성 타임스탬프)에 따라 요청을 충족시키기 위해 어떤 버전을 가져올지 결정합니다. 이러한 접근 방식의 이점은 여러 쿼리가 다른 쿼리가 해당 행을 업데이트하는 동안에도 이전 버전의 행을 읽을 수 있다는 것입니다. 쿼리는 해당 쿼리의 트랜잭션이 시작될 때의 데이터베이스 스냅샷을 찾습니다.
이러한 접근 방식은 명시적인 Record Lock이 필요 없게 하여, Write Transaction이 동일한 항목을 수정하는 동안 Read Transaction이 데이터에 접근하는 것을 차단하지 않습니다.
David Reed의 1978년 MIT 박사 학위 논문 “분산 데이터베이스 시스템에서의 동시성 제어”는 MVCC를 처음으로 설명한 것으로 알려져 있습니다. 최초의 상업용 DBMS에서의 MVCC 구현은 1980년대의 InterBase였습니다. 그 이후로, 트랜잭션을 지원하는 거의 모든 새로운 DBMS는 MVCC를 구현하고 있습니다.
MVCC를 지원하는 DBMS를 구축할 때 시스템 엔지니어는 여러 설계 결정을 내려야 합니다. 고수준에서 보면 다음과 같습니다:
- 기존 행에 대한 업데이트를 어떻게 저장할 것인가.
- 런타임에 쿼리에 대해 올바른 행 버전을 어떻게 찾을 것인가.
- 더 이상 보이지 않는 만료된 버전을 어떻게 제거할 것인가.
이러한 결정은 상호 배타적이지 않습니다. 즉 PostgreSQL의 경우, 1980년대에 첫 번째 질문을 처리하는 방식이 나머지 두 가지에 문제를 일으켰으며, 오늘날까지도 이 문제를 다루고 있습니다.
PostgreSQL의 Multi-Version Concurrency Control(MVCC)
PostgreSQL은 처음부터 다중 버전을 지원하도록 설계되었습니다. PostgreSQL의 MVCC 방식의 핵심 아이디어는 겉보기에는 간단합니다. 테이블의 기존 행을 업데이트하는 쿼리가 실행되면, DBMS는 해당 행을 덮어쓰지 않고 복사하여 새 버전에 변경 사항을 적용합니다. 이를 append-only version storage라고 부릅니다. 하지만 이 접근 방식은 시스템 전반에 걸쳐 몇 가지 복잡한 문제를 초래합니다.
다중 버전 저장 (Multi-Versioned Storage)
PostgreSQL은 모든 행 버전을 동일한 저장 공간에 저장합니다. 기존 튜플을 업데이트할 때 DBMS는 새 행 버전을 위한 빈 슬롯을 테이블에서 확보하고, 현재 버전의 내용을 새 버전으로 복사한 후 변경 사항을 새 버전에 적용합니다. 아래 예시는 사용자가 movies 데이터베이스에서 “Shaolin and Wu Tang”의 개봉 연도를 1985년에서 1983년으로 변경하는 업데이트 쿼리를 실행했을 때의 과정을 보여줍니다.
업데이트 과정
- 기존 튜플을 복사하여 새 버전을 생성하고 변경 사항을 적용합니다.
- 기존 테이블 페이지에 공간이 부족한 경우, PostgreSQL은 새 버전을 새로운 테이블 페이지에 생성합니다.
이렇게 동일한 논리적 행을 나타내는 두 개의 물리적 버전이 생성되면, DBMS는 이러한 버전들의 계보를 기록해야 합니다. PostgreSQL과 같은 MVCC DBMS는 단일 Linked List를 통해 버전 Version Chain을 생성하여 이를 해결합니다.
- N2O(Newest-to-Oldest): 최신 버전이 체인의 헤드에 위치하고 이전 버전으로 연결됩니다.
- O2N(Oldest-to-Newest): 가장 오래된 버전이 체인의 헤드에 위치하고 새 버전으로 연결됩니다.
PostgreSQL은 대부분의 DBMS와 달리 O2N 방식을 사용합니다. 이는 쿼리가 최신 버전을 찾을 때 긴 체인을 탐색해야 할 가능성을 높입니다.
PostgreSQL의 MVCC 문제점
PostgreSQL 개발 초기부터 MVCC 방식에서 두 가지 문제가 존재한다는 사실이 알려졌습니다.
- 업데이트할 때마다 튜플 전체를 복사하는 것은 비효율적입니다.
- 최신 버전을 찾기 위해 모든 Version Chain을 탐색하는 것은 자원 낭비입니다.
이를 해결하기 위해 PostgreSQL은 다음과 같은 최적화를 도입했습니다.
인덱스 항목 추가
- 각 물리적 버전마다 테이블의 인덱스에 별도의 항목을 추가합니다.
- 이를 통해 긴 체인을 탐색하지 않고 최신 버전을 바로 찾을 수 있습니다.
HOT 업데이트(Heap-Only Tuple)
- 업데이트가 테이블의 인덱스에 영향을 미치지 않을 경우, PostgreSQL은 동일한 데이터 페이지에 새 버전을 저장하여 디스크 I/O를 줄입니다.
버전 정리(Vacuum)
- PostgreSQL은 튜플이 업데이트될 때마다 새 버전을 생성합니다. 시스템은 만료된 버전(dead tuples)을 제거하기 위해 Vacuum 프로세스를 실행합니다.
Autovacuum
- Autovacuum은 PostgreSQL이 정기적으로 만료된 튜플을 제거하도록 합니다.
- Autovacuum은 모든 트랜잭션이 해당 버전을 참조하지 않을 때 버전을 “만료됨”으로 간주하고 이를 제거합니다.
하지만 Autovacuum에도 한계가 있습니다.
- 데드 튜플 누적: Autovacuum이 워크로드 속도를 따라잡지 못하면 데드 튜플이 쌓입니다.
- 저장 공간 반환 불가: Autovacuum은 데드 튜플을 제거하지만, 디스크 공간을 반환하지는 않습니다. 디스크 공간을 반환하려면 VACUUM FULL이나 pg_repack 같은 수작업이 필요합니다.
정리: PostgreSQL MVCC의 주요 문제점
문제 1: 버전 복사
- PostgreSQL은 튜플을 업데이트할 때 전체를 복사합니다.
- 반면, MySQL과 Oracle은 델타 저장 방식을 사용하여 변경된 부분만 기록합니다.
문제 2: 테이블 부피 증가
- PostgreSQL의 append-only 저장 방식은 데드 튜플이 디스크 공간을 차지하도록 만듭니다.
- Autovacuum은 이를 제거하지만, 디스크 공간을 효율적으로 반환하지 못합니다.
문제 3: 보조 인덱스 유지 비용
- PostgreSQL은 업데이트 시 모든 인덱스를 수정해야 합니다.
- HOT 업데이트가 가능한 경우를 제외하면, 이는 쿼리 성능에 영향을 미칩니다.
문제 4: Autovacuum 관리
- PostgreSQL의 Autovacuum은 설정하기 복잡하고, 장기 실행 트랜잭션이 이를 방해할 수 있습니다.
- 이는 성능 문제로 이어질 수 있으며, 수작업 개입이 필요할 수 있습니다.
결론
PostgreSQL의 MVCC 구현은 1980년대 설계의 레거시로, 오늘날의 로그 기반 시스템 설계와는 거리가 멉니다. 따라서 특정 워크로드에서는 MySQL이나 Oracle이 더 나은 성능을 제공합니다.
그럼에도 불구하고 PostgreSQL은 여전히 많은 사람들이 선호하는 DBMS입니다. PostgreSQL을 사용하려면 이러한 단점을 이해하고 적절히 관리해야 합니다.
참조
https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html
'ComputerScience > Database' 카테고리의 다른 글
[Database] 캐시 압력(Cache Pressure) (0) | 2024.09.20 |
---|---|
[Database] 분산 트랜잭션과 2PC, SAGA 패턴 (0) | 2024.09.07 |
[Database] CQRS와 분산 읽기/쓰기 DB 구성, 데이터 일관성 모델 (최종 일관성, 강력한 일관성) (0) | 2024.08.13 |
[Database] DBCP (DB connection pool)과 hikariCP, MySQL을 기준으로 (0) | 2024.07.13 |
[Database] DB MVCC와 PostgreSQL, MySQL의 동작 비교 (0) | 2024.04.27 |