2PL
- 다수의 트랜잭션이 동시에 실행될 때, lock이 가능한 단계와 unlock만 가능한 단계를 구분하여 동시성을 제어하는 방법
- 확장단계(Growing Phase): 트랜잭션은 lock 연산만 실행 가능, unlock 연산 불가
- 축소단계(Shrinking Phase): 트랜잭션은 unlock 연산만 실행 가능, lock 연산 불가
- 트랜잭션 실행 단계
- 1단계: 트랜잭션이 필요로 하는 lock 권한들을 얻음
- 2단계: 트랜잭션이 소유한 lock을 해제, 이때 새로운 lock을 요청할 수 없음
2PL 특징
- 트랜잭션이 같은 데이터에 동시에 접근하는 것을 차단하여 직렬화(serializability)를 보장
- 획득하려는 lock을 다른 트랜잭션이 먼저 lock을 획득하고 있는 경우 lock이 해체될 때까지 대기
- DB 일관성이 높은 수준으로 보장됨
- deadlock이 발생할 가능성이 있음
2PL 동작 방식
2개의 트랜잭션이 동시에 수행되는 경우
- X를 갱신하는 Tx1이 실행되는 도중, X를 조회하는 Tx2이 실행됨
- Tx2가 S-lock(X)을 획득하기 위해서는, X-lock(X)이 해제될 때까지 대기해야 함
- Tx1에서 X-lock(X)을 해제되면, Tx2은 S-lock(X)을 획득하고 트랜잭션 수행
lock 상태 | Tx1 | Tx2 | lock 상태 |
획득 | X-lock(X) | ||
read X | S-lock(X) | 대기 | |
update X | |||
해제 | X-lock(X) | ||
S-lock(X) | 획득 | ||
read X | |||
S-lock(X) | 해제 |
MVCC
- 트랜잭션을 수행할 때 마다 객체에 대해 여러 버전을 생성해서, 기존의 데이터와 변경된 데이터를 동시에 유지하는 방법
- 생성되는 버전인 MVCCID는 고유 식별자
- 즉 MVCC에서는 하나의 데이터에 대해 여러 버전의 데이터가 존재하고, 사용자는 마지막 버전의 데이터를 읽음
MVCC 특징
- 다른 트랜잭션이 수정 중인 객체를 읽는 것을 허용함
- 2PL 방식과 비교했을 때, read/write 동시성을 허용하기 때문에 빠른 동작 가능
- 새로운 버전의 데이터를 지속적으로 만들기 때문에, 사용하지 않는 데이터를 정리하는 시스템이 필요 → VACUUM
MVCC 구현 방식
- snapshot을 통해 각각의 MVCCID 마다 데이터의 변경사항을 저장
- snapshot: 이전 버전의 데이터와 비교해서 변경된 내용을 기록
- 가장 최근에 commit된 MVCCID에서 1을 증가시킨 값을 새로운 MVCCID로 할당
- 최근 commit된 MVCCID와 snapshot을 확인하기 위해 활성 트랜잭션 목록을 유지
- 레코드를 insert한 트랜잭션과 delete한 트랜잭션을 구분하기 위해, insert MVCCID와 delete MVCCID를 기록함
- MVCC flag로 레코드의 상태를 판별
- 1: insert
- 3: delete
- 5: update
- 7: update → delete
MVCC 동작 방식
새로운 레코드를 insert하는 경우
- a=1를 테이블에 insert
insert into test_tbl values(1);
- 새로운 버전을 생성하고 MVCCID를 insert_id로 설정
- 해당 트랜잭션이 commit 될 때까지 다른 트랜잭션은 추가된 레코드를 읽을 수 없음
레코드를 update하는 경우
- a=2 → a=3로 update
update test_tbl set a=3 where a=2;
- 레코드를 업데이트하고 이전 값을 log에 저장
- 해당 레코드의 MVCCID를 새로운 버전으로 변경하고, prev_version을 설정
- 데이터가 변경되고 나서 이전의 데이터는 log에 저장됨
레코드를 delete하는 경우
- a=1인 레코드를 delete
delete test_tbl where a=1;
- 기존의 버전을 삭제하지 않고, MVCCID를 delete_mvccid로 설정
- 이는 타 트랜잭션이 해당 레코드를 식별 가능하게 함
MVCC에서 데이터를 읽는 방식
- 자신의 MVCCID보다 상위 버전을 읽을 수 없음
- 생성된 MVCCID가 다음과 같을 때, MCVVID마다 읽는 데이터는 다음과 같음
- 현재 MVCCID가 188일 때
a 1 - 현재 MVCCID가 189일 때, log에 저장된 값인 2를 읽어옴
a 1 2 - 현재 MVCCID가 190일 때
a 1 3 - 현재 MVCCID가 191일 때
a 1
VACUUM
- MVCC에서 기존 버전을 유지하면서 새 버전을 지속적으로 생성하면, 데이터베이스 크기가 무한으로 증가하는 문제가 발생
- 사용하지 않는 이전 데이터를 제거하기 위한 시스템이 필요
- 각각의 행 버전은 다음의 단계를 거침
- 레코드를 insert하는 트랜잭션을 수행 시 commit 되지 않았으면, 해당 트랜잭션만 레코드를 조회할 수 있음
- (1)의 트랜잭션이 commit 되는 시점 이전의 트랜잭션은 해당 레코드를 조회할 수 없음, 이후 시점의 트랜잭션은 조회 가능
- 레코드를 delete하는 트랜잭션을 수행 시 commit 되지 않았으면, 다른 트랜잭션은 해당 레코드를 조회 가능, delete 트랜잭션은 레코드를 볼 수 없음
- (3)의 트랜잭션이 commit 되는 시점 이전의 트랜잭션은 해당 레코드를 볼 수 있고, 이후 트랜잭션은 볼 수 없음
- 모든 활성화 트랜잭션이 완료될 때까지 볼 수 없음
- 데이터 베이스에서 제거
- vacuum은 활성화 되지 않은 삭제된 레코드의 버전을 제거하기 위해 가져옴
VACUUM 원칙
- 정확하고 안전해야함
- Active한 데이터를 제거하면 안되고, 더 이상 사용하지 않는 데이터를 놓치면 안됨
- 신중해야함
- 정리 프로세스가 데이터베이스의 내용을 변경하므로, 실제 트랜잭션 활동에 간섭을 최소화 해야함
- 빠르고 효율적이어야 함
VACUUM 구현 방식
복구 로깅
- 복구 로깅에는 힙 및 인덱스 변경에 대한 복구 데이터 주소를 유지 → 데이터베이스를 스캔하지 않고 VACUUM이 대상으로 바로 이동 가능
- log 데이터의 처리는 활성 worker의 작업에 간섭을 일으키지 않음
- VACUUM은 처리할 로그 항목을 MVCCID를 기반으로 결정
- 각 트랜잭션은 활성화된 가장 오래된 MVCCID를 유지, 해당 MVCCID보다 낮은 버전은 삭제할 수 있음
병렬 수행
- 로그 데이터를 고정 크기 블록으로 분할하고, 각 블록마다 하나의 vacuum 작업을 생성하여 병렬 처리
- vacuum 작업은 로그 블록에 있는 관련 로그 항목을 기반으로 데이터 공간을 회수하는 VACUUM Worker 들에 의해 선택
- 로그 블록의 추적 및 vacuum 작업의 생성은 VACUUM Master가 수행
VACUUM 데이터
- 로그 블록에서 수집된 데이터는 vacuum 데이터 파일에 저장 → vacuum 데이터 파일은 VACUUM 작업을 수행할 때까지 유지됨
- 수집된 로그 블록 데이터는 ‘래치-프리(latch-free) 버퍼 → vacuum 데이터 파일’을 거쳐 저장
- 래치-프리(latch-free) 버퍼는 vacuum 시스템에서 작업 중인 스레드(로그 블록 및 수집 데이터 생성)들의 간섭을 피하기 위해 사용
- VACUUM Master가 주기적으로 버퍼에 있는 모든 내용을 vacuum 데이터로 저장
'DBMS > CUBRID' 카테고리의 다른 글
[CUBRID] 백업과 복구의 이해 (0) | 2024.08.26 |
---|---|
[CUBRID] VACUUM 테스트 (0) | 2024.08.25 |
[CUBRID] Lock 관련 파라미터 (0) | 2024.08.25 |
[CUBRID] Lock 발생시킨 후 잠금 상태 확인 (0) | 2024.08.25 |
[CUBRID] 잠금과 교착 상태 (1) | 2024.06.01 |