다중 버전 동시성 제어
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
다중 버전 동시성 제어(MVCC)는 데이터베이스에서 여러 버전의 데이터를 유지하여 동시성을 관리하는 기술이다. 1981년 필립 A. 번스타인과 네이선 굿맨의 논문에서 자세히 설명되었으며, 1984년 출시된 VAX Rdb/ELN이 최초의 상용 MVCC 데이터베이스로 알려져 있다. MVCC는 각 데이터 항목의 여러 복사본을 유지하여, 각 사용자가 특정 시점의 데이터베이스 스냅샷을 볼 수 있도록 한다. 이 방식은 읽기 트랜잭션이 쓰기 작업에 의해 차단되지 않도록 하여 동시 읽기-쓰기를 가능하게 한다. MVCC는 타임스탬프 또는 트랜잭션 ID를 사용하여 트랜잭션 일관성을 유지하며, 스냅샷 격리를 구현하는 데 효과적이다. 리싱크DB, 버클리 DB, MySQL (InnoDB 엔진 사용 시) 등 다양한 데이터베이스에서 MVCC를 지원하며, 클로저의 소프트웨어 트랜잭션 메모리 및 여러 버전 관리 시스템에서도 활용된다.
더 읽어볼만한 페이지
- 동시성 제어 알고리즘 - 데커의 알고리즘
데커 알고리즘은 두 프로세스 간 상호 배제를 해결하기 위해 만들어진 최초의 소프트웨어 기반 알고리즘 중 하나로, 공유 자원에 대한 동시 접근을 막고 임계 영역 진입 우선순위를 조정하지만, 현대 운영체제에서는 효율성 문제로 잘 사용되지 않으나 상호 배제 개념 이해에 기여한다. - 동시성 제어 알고리즘 - 스핀락
스핀락은 락 획득을 위해 반복적으로 확인하며 대기하는 동기화 메커니즘으로, 성능 향상을 위한 최적화 기법과 하이퍼 스레딩 환경에서의 성능 향상 기법이 존재하지만, CPU 자원 낭비라는 단점으로 인해 다양한 대안 방식이 모색되고 있다. - 동시성 제어 - 세마포어
세마포어는 데이크스트라가 고안한 정수 변수로, P/V 연산을 통해 자원 접근을 제어하고 동기화 문제를 해결하며, 계수 세마포어와 이진 세마포어로 나뉘어 멀티스레드 환경에서 자원 관리 및 스레드 동기화에 기여한다. - 동시성 제어 - 모니터 (동기화)
모니터는 공유 자원 접근을 제어하여 프로세스 간 동기화를 구현하는 프로그래밍 구조로, 뮤텍스 락, 조건 변수 등으로 구성되어 경쟁 상태를 방지하며 여러 프로그래밍 언어에서 지원된다. - 트랜잭션 처리 - 2단계 커밋 프로토콜
2단계 커밋 프로토콜은 분산 컴퓨팅 환경에서 트랜잭션의 원자성을 보장하는 분산 알고리즘으로, 조정자와 참가자로 구성되어 모든 참가자가 트랜잭션을 완료하거나 아무도 완료하지 못하도록 하며, 커밋 요청 및 커밋 단계를 거쳐 모든 참가자의 동의를 얻어야 커밋된다. - 트랜잭션 처리 - 온라인 트랜잭션 처리
온라인 트랜잭션 처리(OLTP)는 실시간 데이터베이스 트랜잭션 처리 방식으로, 가용성, 속도, 동시성, 내구성을 목표로 은행, 항공사, 전자 상거래 등에서 활용된다.
다중 버전 동시성 제어 | |
---|---|
기본 정보 | |
![]() | |
유형 | 동시성 제어 방법 |
속성 | |
사용 데이터베이스 | 오라클 PostgreSQL MySQL MariaDB 인터베이스 파이어버드 SAP HANA MemSQL 볼트DB Couchbase OrientDB 마이크로소프트 SQL 서버 DB2 |
주요 이점 | 읽기 작업이 쓰기 작업을 차단하지 않음 |
주요 단점 | 더 많은 저장 공간 필요 가비지 컬렉션 오버헤드 발생 가능성 복잡한 구현 |
상세 정보 | |
설명 | 다중 버전 동시성 제어는 데이터베이스 관리 시스템 및 메모리 관리에서 사용되는 동시성 제어 방법임. |
목표 | 데이터 일관성을 보장하면서 여러 사용자가 데이터베이스에 동시에 접근할 수 있도록 허용 각 사용자는 특정 시점의 데이터베이스 스냅샷을 볼 수 있음 |
구현 방식 | 데이터의 여러 버전을 유지 관리 각 트랜잭션은 시작 시점의 데이터베이스 스냅샷을 사용하여 작업 트랜잭션이 데이터를 수정하면 이전 버전을 덮어쓰는 대신 새 버전을 생성 |
장점 | 읽기 작업은 쓰기 작업을 차단하지 않으므로 읽기 성능 향상 쓰기 작업은 읽기 작업을 차단하지 않으므로 쓰기 성능 유지 데드락 발생 가능성 감소 |
단점 | 데이터의 여러 버전을 저장해야 하므로 더 많은 저장 공간 필요 오래된 버전을 정리하는 가비지 컬렉션 오버헤드 발생 가능성 구현 복잡도 증가 |
사용 사례 | 읽기 작업이 많은 워크로드 높은 동시성이 요구되는 시스템 트랜잭션 격리 수준이 높은 시스템 |
2. 역사
다중 버전 동시성 제어(MVCC)는 1981년 필립 번스타인과 네이선 굿맨의 "분산 데이터베이스 시스템의 동시성 제어" 논문에 일부 기술되어 있다.[9] 이 논문은 데이비드 P. 리드의 논문을 언급하며, 해당 논문에서 MVCC를 명확하게 설명하고 있다.[10] 또한, MVCC 개념은 1981년 필립 번스타인과 네이선 굿맨이 ACM 컴퓨팅 서베이지에 게재하였다.
2. 1. 상용 데이터베이스 시스템 적용
다중 버전 동시성 제어는 1981년 필 번스타인과 네이선 굿맨(당시 컴퓨터 코퍼레이션 오브 아메리카 소속)의 논문 "분산 데이터베이스 시스템의 동시성 제어"[3]에 비교적 자세하게 설명되어 있다. 번스타인과 굿맨의 논문은 데이비드 P. 리드가 1978년에 발표한 논문[4]을 인용하고 있으며, 이 논문은 MVCC를 명확하게 설명하고, 이를 독창적인 연구라고 주장한다.MVCC를 특징으로 하는 최초의 상용 데이터베이스 소프트웨어 제품은 1984년에 출시된 VAX Rdb/ELN[5]이며, 짐 스타키가 디지털 이큅먼트 코퍼레이션에서 개발했다. 스타키는 이후[6] 두 번째로 상업적으로 성공한 MVCC 데이터베이스인 인터베이스를 개발했다.[7]
3. 작동 방식
MVCC는 동시성 제어가 없을 때 발생할 수 있는 문제, 즉 다른 사람이 데이터베이스에 쓰는 동안 누군가 읽고 있다면, 읽는 사람이 반쯤 쓰여진 데이터 또는 일관성 없는 데이터를 볼 수 있는 문제를 해결한다. 예를 들어, 두 개의 은행 계좌 간에 송금을 할 때, 읽는 사람이 원래 계좌에서 돈이 인출된 시점과 대상 계좌에 입금되기 전 시점에 은행의 잔액을 읽으면, 은행에서 돈이 사라진 것처럼 보일 수 있다.
격리성은 데이터에 대한 동시 접근을 보장하는 속성으로, 동시성 제어 프로토콜을 통해 구현된다. 가장 간단한 방법은 모든 읽기 작업을 쓰기 작업이 완료될 때까지 기다리게 하는 읽기-쓰기 잠금 방식이지만, 잠금은 특히 긴 읽기 트랜잭션과 업데이트 트랜잭션 간에 자원 경합을 일으킨다.
MVCC는 각 데이터 항목의 여러 복사본을 유지함으로써 이 문제를 해결한다. 데이터베이스에 연결된 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 작성자가 변경한 내용은 트랜잭션이 커밋될 때까지 다른 사용자에게 표시되지 않는다.
MVCC 데이터베이스는 데이터를 업데이트할 때, 이전 데이터를 덮어쓰는 대신 새 버전을 생성한다. 따라서 여러 버전이 저장되며, 각 트랜잭션이 보는 버전은 구현된 격리 수준에 따라 다르다. MVCC에서 가장 일반적인 격리 수준은 스냅샷 격리이며, 트랜잭션 시작 시점의 데이터 상태를 관찰한다.
MVCC는 시점 일관성을 제공한다. 읽기 트랜잭션은 일반적으로 타임스탬프나 트랜잭션 ID를 사용하여 읽을 데이터베이스 상태를 결정하고, 해당 버전의 데이터를 읽는다. 따라서 읽기와 쓰기 트랜잭션은 잠금 없이 서로 격리된다. (일부 MVCC 데이터베이스(예: 오라클)에서는 잠금이 사용되기도 한다.) 쓰기는 새 버전을 만들고, 동시 읽기는 이전 버전에 접근한다.
MVCC는 사용되지 않고 더 이상 읽히지 않을 버전을 제거해야 하는 과제를 안고 있다. PostgreSQL은 [https://www.postgresql.org/docs/9.4/sql-vacuum.html VACUUM FREEZE] 프로세스를 통해 이 문제를 해결할 수 있다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나누어 관리하기도 한다.
MVCC는 타임스탬프('''TS''')와 ''증가하는 트랜잭션 ID''를 사용하여 ''트랜잭션 일관성''을 달성한다. 객체의 각 버전에는 ''읽기 타임스탬프'' ('''RTS''')와 ''쓰기 타임스탬프'' ('''WTS''')가 있어, 트랜잭션('''T''')이 데이터베이스 객체('''P''')를 ''읽기'' 위해 대기할 필요가 없다. 특정 트랜잭션 '''Ti'''는 '''RTS'''('''Ti''')보다 먼저 나오는 객체의 가장 최근 버전을 읽을 수 있다.[2]
트랜잭션 '''Ti'''가 객체 '''P'''에 ''쓰기''를 할 때, 다른 트랜잭션 '''Tk'''가 같은 객체에 대해 발생하면, '''RTS'''('''Ti''') < '''RTS'''('''Tk''')여야 ''쓰기 작업'' ('''WTS''')이 성공한다. 즉, 읽기 타임스탬프 ('''RTS''')가 더 이른 다른 진행 중인 트랜잭션이 있으면 ''쓰기''를 완료할 수 없다.[2]
객체 ('''P''')에 ''쓰기''를 할 때, 트랜잭션의 ''타임스탬프'' ('''TS''')가 객체의 현재 읽기 타임스탬프보다 이르면 ('''TS'''('''Ti''') < '''RTS'''('''P''')), 트랜잭션은 중단되고 다시 시작된다. 그렇지 않으면, '''Ti'''는 객체 '''P'''의 새 버전을 생성하고 새 버전의 읽기/쓰기 타임스탬프 '''TS'''를 '''TS'''('''Ti''')로 설정한다.[2]
MVCC는 쓰기 처리(트랜잭션)가 진행되는 동안 다른 사용자가 읽기 접근을 시도했을 때, 쓰기 직전의 상태(스냅샷)를 반환한다. 즉, 쓰기 중에도 읽기가 가능하며, 읽기 중에도 쓰기가 가능하다.
MVCC에서 가용성을 확보하려면 모든 처리 순서를 확실하게 기록해야 하며, 이를 위해 타임스탬프나 트랜잭션 ID 등을 사용하여 모든 갱신 처리가 관리된다.
MVCC는 SQL 규격(SQL92)에서 정해진 4가지 종류의 트랜잭션 격리 수준 중 '''Read Committed''' (커밋된 데이터는 항상 읽음)에 해당한다.
3. 1. 트랜잭션 관리
동시성 제어가 없으면, 여러 사용자가 동시에 데이터베이스에 접근할 때 문제가 발생할 수 있다. 예를 들어, 한 사용자가 데이터를 읽는 동안 다른 사용자가 데이터를 변경하면, 읽는 사용자는 불완전하거나 일관성 없는 데이터를 볼 수 있다. 은행 계좌 이체시, 돈이 빠져나간 시점과 입금되기 전 시점 사이에 잔액을 조회하면 돈이 사라진 것처럼 보이는 경우가 그 예시이다.[2]격리성은 이러한 동시 접근 문제를 해결하는 속성으로, 동시성 제어 프로토콜을 통해 구현된다. 가장 간단한 방법은 읽기-쓰기 잠금을 사용하여 쓰기 작업이 완료될 때까지 모든 읽기 작업을 기다리게 하는 것이지만, 잠금은 자원 경합을 발생시킬 수 있다.[2]
MVCC는 각 데이터 항목의 여러 버전을 유지하여 이 문제를 해결한다. 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 변경 사항은 트랜잭션이 커밋될 때까지 다른 사용자에게 보이지 않는다.[2]
MVCC 데이터베이스는 데이터를 업데이트할 때 이전 데이터를 덮어쓰는 대신 새 버전을 생성한다. 여러 버전이 저장되므로, 각 트랜잭션은 구현된 격리 수준에 따라 다른 버전을 볼 수 있다. 가장 일반적인 격리 수준은 스냅샷 격리이며, 트랜잭션 시작 시점의 데이터 상태를 관찰한다.[2]
MVCC는 시점 일관성을 제공한다. 읽기 트랜잭션은 타임스탬프나 트랜잭션 ID를 사용하여 읽을 데이터 버전을 결정하고, 읽기 및 쓰기 트랜잭션은 잠금 없이 서로 격리된다. 그러나 일부 MVCC 데이터베이스(예: 오라클)에서는 잠금이 사용되기도 한다. 쓰기는 새 버전을 만들고, 동시 읽기는 이전 버전에 접근한다.[2]
MVCC는 사용되지 않는 버전을 제거해야 하는 과제를 안고 있다. PostgreSQL은 [https://www.postgresql.org/docs/9.4/sql-vacuum.html VACUUM FREEZE] 프로세스를 통해 이 문제를 해결할 수 있다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나누어 관리하기도 한다.[2]
MVCC는 타임스탬프('''TS''')와 ''증가하는 트랜잭션 ID''를 사용하여 ''트랜잭션 일관성''을 유지한다. 객체의 각 버전에는 ''읽기 타임스탬프'' ('''RTS''')와 ''쓰기 타임스탬프'' ('''WTS''')가 있어, 트랜잭션('''T''')이 데이터베이스 객체('''P''')를 ''읽기'' 위해 대기할 필요가 없다. 특정 트랜잭션 '''Ti'''는 '''RTS'''('''Ti''')보다 먼저 나오는 객체의 가장 최근 버전을 읽을 수 있다.[2]
트랜잭션 '''Ti'''가 객체 '''P'''에 ''쓰기''를 할 때, 다른 트랜잭션 '''Tk'''가 같은 객체에 대해 발생하면, '''RTS'''('''Ti''') < '''RTS'''('''Tk''')여야 ''쓰기 작업'' ('''WTS''')이 성공한다. 즉, 읽기 타임스탬프 ('''RTS''')가 더 이른 다른 진행 중인 트랜잭션이 있으면 ''쓰기''를 완료할 수 없다.[2]
객체 ('''P''')에 ''쓰기''를 할 때, 트랜잭션의 ''타임스탬프'' ('''TS''')가 객체의 현재 읽기 타임스탬프보다 이르면 ('''TS'''('''Ti''') < '''RTS'''('''P''')), 트랜잭션은 중단되고 다시 시작된다. 그렇지 않으면, '''Ti'''는 객체 '''P'''의 새 버전을 생성하고 새 버전의 읽기/쓰기 타임스탬프 '''TS'''를 '''TS'''('''Ti''')로 설정한다.[2]
이 시스템의 단점은 여러 버전의 데이터를 저장하는 비용이 발생한다는 것이다. 그러나 읽기는 절대 차단되지 않기 때문에 데이터베이스에서 값을 읽는 것이 주된 작업 부하에 중요할 수 있다. MVCC는 스냅샷 격리를 구현하는 데 특히 효과적이다.[2]
MVCC는 쓰기 처리(트랜잭션)가 진행되는 동안 다른 사용자가 읽기 접근을 시도했을 때, 쓰기 직전의 상태(스냅샷)를 반환한다. 즉, 쓰기 중에도 읽기가 가능하고, 읽기 중에도 쓰기가 가능하다.
MVCC에서 가용성을 확보하려면 모든 처리 순서를 확실하게 기록해야 하며, 이를 위해 타임스탬프나 트랜잭션 ID 등을 사용하여 모든 갱신 처리가 관리된다.
MVCC는 SQL 규격(SQL92)에서 정해진 4가지 종류의 트랜잭션 격리 수준 중 '''Read Committed''' (커밋된 데이터는 항상 읽음)에 해당한다.
3. 2. 트랜잭션 격리 수준
MVCC는 데이터 항목의 여러 버전을 유지하여 자원 경합 문제를 해결한다. 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 작성자의 변경 내용은 트랜잭션이 커밋될 때까지 다른 사용자에게 표시되지 않는다.MVCC 데이터베이스는 데이터를 업데이트할 때, 이전 데이터를 덮어쓰는 대신 새 버전을 생성한다. 각 트랜잭션이 보는 버전은 구현된 트랜잭션 격리 수준에 따라 다르다. 가장 일반적인 격리 수준은 스냅샷 격리이다. 스냅샷 격리를 사용하면 트랜잭션은 시작 시점의 데이터 상태를 보게 된다.
MVCC는 시점 일관성을 제공한다. 읽기 트랜잭션은 타임스탬프나 트랜잭션 ID를 사용하여 읽을 DB 상태를 결정하고, 해당 버전의 데이터를 읽는다. 따라서 읽기 및 쓰기 트랜잭션은 격리된다. 쓰기는 새 버전을 만들고, 동시 읽기는 이전 버전에 접근한다.
MVCC는 사용되지 않는 버전을 제거해야 하는 과제가 있다. PostgreSQL은 [https://www.postgresql.org/docs/9.4/sql-vacuum.html VACUUM FREEZE] 프로세스를 통해 이 문제를 해결할 수 있다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나누어 이전 버전을 재구성할 수 있게 한다.
MVCC는 쓰기 처리(트랜잭션)가 진행되는 동안 다른 사용자가 읽기를 시도하면 쓰기 직전의 상태(스냅샷)를 반환한다. 즉, 쓰기 중에도 읽기가 가능하고, 읽기 중에도 쓰기가 가능하다.
MVCC에서 가용성을 확보하기 위해 모든 갱신 처리는 타임스탬프나 트랜잭션 ID 등으로 관리된다.
SQL 규격(SQL92)에서 정해진 4가지 종류의 트랜잭션 격리 수준 중 '''Read Committed''' (커밋된 데이터는 항상 읽음)에 해당한다.
4. 구현
MVCC는 동시성 제어가 없을 때 발생할 수 있는 문제, 즉 다른 사람이 데이터베이스에 쓰는 동안 읽는 사람이 불완전하거나 일관성 없는 데이터를 보는 문제를 해결한다. 예를 들어, 두 은행 계좌 간 송금 시 읽는 사람이 특정 시점에 잔액을 조회하면 돈이 사라진 것처럼 보일 수 있다. 격리성은 데이터에 대한 동시 접근을 보장하는 속성이며, 동시성 제어 프로토콜을 통해 구현된다.
가장 간단한 구현 방법은 모든 읽기 작업을 쓰기 작업이 완료될 때까지 기다리게 하는 것이다. 이는 읽기-쓰기 잠금으로 알려져 있다. 그러나 잠금은 특히 긴 읽기 트랜잭션과 업데이트 트랜잭션 간에 자원 경합을 발생시키는 것으로 알려져 있다.
MVCC는 각 데이터 항목의 여러 버전을 유지함으로써 이 문제를 해결한다. 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 작성자가 변경한 내용은 변경이 완료될 때까지(트랜잭션이 커밋될 때까지) 다른 사용자에게 표시되지 않는다.
데이터를 업데이트할 때, MVCC 데이터베이스는 원래의 데이터 항목을 새 데이터로 덮어쓰는 대신, 데이터 항목의 새 버전을 생성한다. 따라서 여러 버전이 저장되며, 각 트랜잭션이 보는 버전은 구현된 격리 수준에 따라 달라진다. MVCC로 구현되는 가장 일반적인 격리 수준은 스냅샷 격리이다. 스냅샷 격리를 사용하면 트랜잭션은 트랜잭션이 시작될 때의 데이터 상태를 관찰한다.
MVCC는 시점 일관성 보기를 제공한다. 읽기 트랜잭션은 일반적으로 타임스탬프 또는 트랜잭션 ID를 사용하여 읽을 DB의 상태를 결정하고, 해당 버전의 데이터를 읽는다. 따라서 읽기 및 쓰기 트랜잭션은 잠금 없이 서로 격리된다. 그러나 잠금이 불필요함에도 불구하고, 일부 MVCC 데이터베이스(예: 오라클)에서는 사용되기도 한다. 쓰기는 새 버전을 만들고, 동시 읽기는 이전 버전에 접근한다.
MVCC는 사용되지 않고 더 이상 읽히지 않을 버전을 제거해야 하는 과제를 제시한다. 어떤 경우에는 쓸모없는 버전을 주기적으로 스캔하여 삭제하는 프로세스가 구현된다. PostgreSQL은 이 접근 방식을 [https://www.postgresql.org/docs/9.4/sql-vacuum.html VACUUM FREEZE] 프로세스와 함께 사용할 수 있다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나눈다. 데이터 부분은 항상 마지막으로 커밋된 버전을 유지하며, 실행 취소 로그는 데이터의 이전 버전을 재구성할 수 있도록 한다.
MVCC는 타임스탬프와 증가하는 트랜잭션 ID를 사용하여 ''트랜잭션 일관성''을 달성한다. 객체의 여러 버전을 유지하여 트랜잭션이 데이터베이스 객체를 읽기 위해 대기할 필요가 없도록 한다. 객체의 각 버전에는 ''읽기 타임스탬프''와 ''쓰기 타임스탬프''가 있어, 특정 트랜잭션이 읽기 타임스탬프보다 먼저 나오는 가장 최근 버전을 읽을 수 있다.
Rust영어에서 MVCC를 사용하는 데이터베이스의 행을 보관하는 구조는 다음과 같다.
```rust
struct Record {
/// 삽입 트랜잭션 식별자 스탬프.
insert_transaction_id: u32,
/// 삭제 트랜잭션 식별자 스탬프.
delete_transaction_id: u32,
/// 데이터의 길이.
data_length: u16,
/// 레코드의 내용.
data: Vec
}
4. 1. 쓰기 작업 규칙
MVCC에서 트랜잭션 '''Ti'''가 객체 '''P'''에 쓰기를 수행할 때, 다른 트랜잭션 '''Tk'''가 이미 해당 객체를 읽고 있다면(즉, '''RTS'''('''Tk''')가 존재하는 경우), 쓰기 작업의 성공 여부는 다음과 같은 규칙을 따른다.
위 조건을 만족하면, '''Ti'''는 객체 '''P'''의 새 버전을 생성하고, 새 버전의 읽기/쓰기 타임스탬프('''TS''')를 트랜잭션의 타임스탬프로 설정한다. ('''TS''' ← '''TS'''('''Ti'''))[2]
간단히 말해, MVCC는 쓰기 작업 중에도 읽기를 허용하고, 읽기 작업 중에도 쓰기를 허용한다. 이를 위해 타임스탬프나 트랜잭션 ID를 사용하여 모든 갱신 처리를 관리한다.
4. 2. 장점 및 단점
MVCC는 동시성 제어 없이 발생할 수 있는 문제, 즉 다른 사용자가 데이터베이스에 쓰는 동안 읽는 사람이 반쯤 쓰여진 데이터나 일관성 없는 데이터를 보는 문제를 해결한다. 예를 들어, 은행 계좌 간 송금 시 읽는 사람이 특정 시점에 잔액을 읽으면 돈이 사라진 것처럼 보일 수 있다. 격리성은 동시 접근을 보장하는 속성이며, 동시성 제어 프로토콜을 통해 구현된다.
가장 간단한 방법은 읽기-쓰기 잠금처럼 모든 읽기 작업을 작성 작업 완료까지 기다리게 하는 것이다. 그러나 잠금은 자원 경합을 발생시킨다. MVCC는 각 데이터 항목의 여러 버전을 유지하여 이 문제를 해결한다. 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 작성자의 변경 내용은 트랜잭션이 커밋될 때까지 다른 사용자에게 보이지 않는다.
데이터 업데이트 시, MVCC는 이전 데이터를 덮어쓰는 대신 새 버전을 생성한다. 여러 버전이 저장되며, 각 트랜잭션이 보는 버전은 구현된 격리 수준에 따라 다르다. 가장 일반적인 격리 수준은 스냅샷 격리이며, 트랜잭션 시작 시점의 데이터 상태를 관찰한다.
MVCC는 시점 일관성 보기를 제공하며, 읽기 및 쓰기 트랜잭션은 잠금 없이 서로 격리된다. 그러나 일부 MVCC 데이터베이스(예: 오라클)에서는 잠금이 사용되기도 한다. 쓰기는 새 버전을 만들고, 동시 읽기는 이전 버전에 접근한다.
MVCC는 사용되지 않는 버전을 제거해야 하는 과제를 제시한다. PostgreSQL은 [https://www.postgresql.org/docs/9.4/sql-vacuum.html VACUUM FREEZE] 프로세스를 사용한다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나누어 관리한다.
MVCC는 타임스탬프와 증가하는 트랜잭션 ID를 사용하여 ''트랜잭션 일관성''을 달성한다. 객체의 각 버전에는 ''읽기 타임스탬프''와 ''쓰기 타임스탬프''가 있어, 특정 트랜잭션이 읽기 타임스탬프보다 먼저 나오는 가장 최근 버전을 읽을 수 있다.
트랜잭션이 객체에 쓰기를 원할 때, 다른 트랜잭션이 있으면 쓰기 작업이 성공하기 위해 해당 트랜잭션의 읽기 타임스탬프가 다른 트랜잭션의 읽기 타임스탬프보다 먼저 와야한다. 즉, 읽기 타임스탬프가 더 이른 다른 트랜잭션이 있으면 쓰기를 완료할 수 없다.
객체에 쓰기를 할 때 트랜잭션의 타임스탬프가 객체의 현재 읽기 타임스탬프보다 이르면, 트랜잭션은 중단되고 다시 시작된다. 그렇지 않으면 객체의 새 버전을 생성하고 새 버전의 읽기/쓰기 타임스탬프를 트랜잭션의 타임스탬프로 설정한다.[2]
MVCC의 단점은 여러 버전의 데이터를 저장하는 비용이다. 그러나 읽기는 절대 차단되지 않으므로 읽기 작업이 많은 경우에 유리하다. MVCC는 스냅샷 격리 구현에 특히 능숙하다.
MVCC는 쓰기 처리가 진행되는 동안 다른 사용자가 읽기 접근을 시도했을 때, 쓰기 직전의 상태(스냅샷)를 반환한다. 즉, 쓰기 중에도 읽기가 가능하며, 읽기 중에도 쓰기가 가능하다. MVCC에서 가용성을 달성하기 위해 모든 갱신 처리는 타임스탬프나 트랜잭션 ID 등으로 관리된다.
SQL 규격(SQL92)의 트랜잭션 격리 수준 중 '''Read Committed'''(커밋된 데이터는 항상 읽음)에 해당한다.
5. 예시
다중 버전 동시성 제어(MVCC)의 작동 방식을 예시를 통해 살펴보자.
시간 = 1일 때 데이터베이스의 상태는 다음과 같다.
시간 | 객체 1 | 객체 2 |
---|---|---|
0 | T0에 의해 "Foo" | T0에 의해 "Bar" |
1 | T1에 의해 "Hello" |
T0은 객체 1 = "Foo"와 객체 2 = "Bar"를 썼다. 그 후 T1은 객체 1 = "Hello"를 썼고 객체 2는 원래 값을 유지했다.
T1이 커밋된 후, T2와 T3 트랜잭션이 동시에 실행된다고 가정하자. T2는 객체 1과 객체 2를 읽는 장기 실행 트랜잭션이고, T3는 객체 2를 삭제하고 객체 3 = "Foo-Bar"를 추가하는 트랜잭션이다. 시간 2에서 데이터베이스 상태는 다음과 같다.
시간 | 객체 1 | 객체 2 | 객체 3 |
---|---|---|---|
0 | T0에 의해 "Foo" | T0에 의해 "Bar" | |
1 | T1에 의해 "Hello" | ||
2 | T3에 의해 (삭제됨) | T3에 의해 "Foo-Bar" |
T2는 T3가 쓰기를 커밋하기 전의 데이터베이스 버전을 보게 되므로, T2는 객체 2 = "Bar"와 객체 1 = "Hello"를 읽는다. 이것이 MVCC가 잠금 없이 스냅샷 격리 읽기를 허용하는 방식이다.
5. 1. 동시 읽기-쓰기
MVCC는 쓰기 처리(트랜잭션)가 진행되는 동안 다른 사용자가 읽기 접근을 시도했을 때, 쓰기 직전의 상태(스냅샷)를 처리 결과로 반환한다. 즉, 쓰기 중에도 읽기가 가능하며, 읽기 중에도 쓰기가 가능하다.시간 = 1일 때 데이터베이스의 상태는 다음과 같다.
시간 | 객체 1 | 객체 2 |
---|---|---|
0 | T0에 의해 "Foo" | T0에 의해 "Bar" |
1 | T1에 의해 "Hello" |
T0은 객체 1 = "Foo"와 객체 2 = "Bar"를 썼다. 그 후 T1은 객체 1 = "Hello"를 썼고 객체 2는 원래 값을 유지했다. 객체 1의 새 값은 T1이 커밋된 후 시작되는 모든 트랜잭션에 대해 시간 0의 값을 대체하며, 이 시점에서 객체 1의 버전 0은 가비지 수집될 수 있다.
T1이 커밋된 후 객체 2와 객체 1에 대한 읽기 작업을 시작하는 장기 실행 트랜잭션 T2와 객체 2를 삭제하고 객체 3 = "Foo-Bar"를 추가하는 동시 업데이트 트랜잭션 T3이 있는 경우, 시간 2에서 데이터베이스 상태는 다음과 같다.
시간 | 객체 1 | 객체 2 | 객체 3 |
---|---|---|---|
0 | T0에 의해 "Foo" | T0에 의해 "Bar" | |
1 | T1에 의해 "Hello" | ||
2 | T3에 의해 (삭제됨) | T3에 의해 "Foo-Bar" |
시간 2 시점에 삭제로 표시된 객체 2의 새 버전과 새로운 객체 3이 있다. T2와 T3이 동시에 실행되므로 T2는 2 이전, 즉 T3이 쓰기를 커밋하기 전의 데이터베이스 버전을 보게 되므로 T2는 객체 2 = "Bar"와 객체 1 = "Hello"를 읽는다. 이것이 다중 버전 동시성 제어가 록 없이 스냅샷 격리 읽기를 허용하는 방식이다.
MVCC에서 가용성을 달성하기 위해서는 최소한 모든 처리의 "어떤 순서"로 수행되었는지를 확실하게 기록해야 한다. 이를 위해 타임스탬프나 트랜잭션 ID 등을 사용하여 모든 갱신 처리가 관리된다.
SQL 규격(SQL92)에서 정해진 4가지 종류의 트랜잭션 격리 수준 중에서는, '''Read Committed''' (커밋된 데이터는 항상 읽음)에 해당하는 처리이다.
6. MVCC를 채택한 데이터베이스 및 소프트웨어
MVCC는 클로저(Clojure)의 소프트웨어 트랜잭션 메모리, 버전 관리 시스템 등 다양한 소프트웨어에서 사용된다.
6. 1. 데이터베이스
동시성 제어가 없으면, 여러 사용자가 동시에 데이터베이스에 접근할 때 데이터 불일치 문제가 발생할 수 있다. 예를 들어, 은행 계좌 이체 중 잔액을 조회하면 돈이 사라진 것처럼 보일 수 있다. 격리성은 이러한 동시 접근 문제를 해결하는 속성이며, 동시성 제어 프로토콜을 통해 구현된다. 가장 간단한 방법은 읽기-쓰기 잠금을 사용하여 쓰기 작업이 완료될 때까지 읽기 작업을 대기시키는 것이다. 그러나 잠금은 자원 경합을 유발할 수 있다.MVCC는 각 데이터 항목의 여러 버전을 유지하여 이 문제를 해결한다. 각 사용자는 특정 시점의 데이터베이스 ''스냅샷''을 보게 되며, 작성자의 변경 내용은 트랜잭션이 커밋될 때까지 다른 사용자에게 보이지 않는다.
MVCC 데이터베이스는 데이터를 업데이트할 때 이전 데이터를 덮어쓰지 않고 새 버전을 생성한다. 트랜잭션이 보는 버전은 구현된 격리 수준에 따라 다르며, 가장 일반적인 격리 수준은 스냅샷 격리이다. 스냅샷 격리를 사용하면 트랜잭션은 시작 시점의 데이터 상태를 보게 된다.
MVCC는 시점 일관성을 제공하며, 읽기 및 쓰기 트랜잭션은 잠금 없이 서로 격리된다. 그러나 일부 MVCC 데이터베이스(예: 오라클)에서는 잠금이 사용되기도 한다.
MVCC는 사용되지 않는 버전을 제거해야 하는 과제를 안고 있다. PostgreSQL은 VACUUM FREEZE 프로세스를 통해 이 문제를 해결할 수 있다. 다른 데이터베이스는 저장 블록을 데이터 부분과 실행 취소 로그로 나누어 관리한다.
다중 버전 동시성 제어는 1981년 필 번스타인과 네이선 굿맨의 논문에 자세히 설명되어 있다.[3] 이 논문은 1978년 데이비드 P. 리드의 논문[4]을 인용하며, MVCC를 독창적인 연구로 주장한다.
최초의 상용 MVCC 데이터베이스는 1984년 VAX Rdb/ELN[5]이며, 짐 스타키가 개발했다. 스타키는 이후[6] 인터베이스를 개발했다.[7]
MVCC를 지원하는 데이터베이스는 다음과 같다.
데이터베이스 | 지원 시작 버전 |
---|---|
리싱크DB | 1.0 |
버클리 DB | |
DB2 | 9.7 (CS with CC) |
파이어버드 | |
H2 데이터베이스 | |
인터베이스 | |
마이크로소프트 SQL 서버 | 2005 (READ_COMMITTED_SNAPSHOT) |
MySQL | InnoDB 엔진 사용 시 |
오라클 데이터베이스 | 4 |
SAP IQ | |
PostgreSQL | 6.5 |
조프 객체 데이터베이스 | |
오리엔트DB |
6. 2. 기타 소프트웨어
MVCC는 다음을 포함한 여러 소프트웨어 시스템에서 사용된다.- 클로저(Clojure)의 소프트웨어 트랜잭션 메모리
- 버전 관리 시스템 전반
7. 추가 문헌
- Gerhard Weikum, Gottfried Vossen, ''Transactional information systems: theory, algorithms, and the practice of concurrency control and recovery'', Morgan Kaufmann, 2002, ISBN 1-55860-508-8
참조
[1]
웹사이트
Clojure - Refs and Transactions
https://clojure.org/[...]
2019-04-12
[2]
서적
Database management systems
Osborne/McGraw-Hill
[3]
논문
Concurrency Control in Distributed Database Systems
http://portal.acm.or[...]
[4]
Thesis
Naming and Synchronization in a Decentralized Computer System
http://hdl.handle.ne[...]
2022-11-12
[5]
뉴스
RDB Gets Mixed Greeting
https://books.google[...]
1984-04-09
[6]
웹사이트
Re: [Firebird-devel] isc_dpb_dbkey_scope | Firebird
https://sourceforge.[...]
[7]
웹사이트
A not-so-very technical discussion of Multi Version Concurrency Control
https://firebirdsql.[...]
2020-11-12
[8]
웹사이트
refs
http://clojure.org/r[...]
2013-09-18
[9]
웹인용
Concurrency Control in Distributed Database Systems
http://portal.acm.or[...]
[10]
웹인용
Naming and Synchronization in a Decentralized Computer System
https://web.archive.[...]
1978-09-21
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com