Oracle/Oracle Performance

Transction Multi-Version Concurrency Control(MVCC) 동시성 및 정합성

의미와의미 2025. 1. 10. 15:42
반응형

MVCC(다중 버전 동시성 제어)

Multi-Version Concurrency Control는 데이터베이스 관리 시스템(DBMS)에서 동시성과 일관성을 관리하기 위한 기술입니다.

MVCC는 동시에 여러 트랜잭션이 데이터에 접근할 때 충돌을 최소화하고, 읽기와 쓰기를 효율적으로 처리하도록 설계되었습니다.

 

MVCC의 장점

읽기 성능 향상: 읽기 작업이 잠금 없이 수행되므로 높은 동시성을 제공합니다.

일관성 유지: 각 트랜잭션은 자신만의 스냅샷에서 작업하므로 데이터 일관성이 보장됩니다.

쓰기 작업 격리: 트랜잭션 간 쓰기 충돌이 발생하더라도 시스템이 이를 안전하게 관리합니다.

 

MVCC의 단점

스토리지 오버헤드: 데이터의 여러 버전을 유지하므로 저장 공간이 많이 필요합니다.

Garbage Collection 필요: 사용되지 않는 이전 버전을 삭제하거나 정리해야 합니다. (SERIALIZABLE)

복잡성 증가: 구현이 복잡하며, 성능 튜닝이 어려울 수 있습니다.

MVCC의 버전 관리:
MVCC는 데이터의 여러 버전을 유지합니다.
트랜잭션이 데이터를 읽을 때, 특정 시점의 데이터를 읽도록 보장하여 다른 트랜잭션의 쓰기로 인한 영향을 받지 않습니다.
 

1.스냅샷 읽기 (Snapshot Read):
트랜잭션은 자신이 시작된 시점의 데이터를 읽습니다.
이는 트랜잭션 간의 읽기-쓰기 충돌을 방지하고 일관된 읽기 작업을 보장합니다.
 
\
2. 쓰기 시 충돌 해결:
두 트랜잭션이 같은 데이터를 동시에 수정하려 할 때, MVCC는 충돌을 감지하고 하나의 트랜잭션만 성공시킵니다.
 

3. 트랜잭션 격리 수준 지원:
MVCC는 트랜잭션 격리 수준(: Read Uncommitte,Read Committed, Repeatable Read, Serializable)을 지원합니다. 특정 격리 수준에 따라 트랜잭션이 볼 수 있는 데이터의 버전이 결정됩니다.

 

DBMS 격리수준 지원여부

트랜잭션 격리수준 Oracle PostgresSQL MySQL MSSQL
Read Uncommited 미지원 지원 지원(InnoDB) 지원
Read Commited 기본값 기본값 지원(InnoDB) 기본값
Repeatable Read 미지원 지원 기본값(InnoDB) 지원
Serializable 지원 지원 지원(InnoDB) 지원

 

Read Uncommitted : 다른 트랜잭션에서 커밋되지 않은 데이터를 읽을 수 있음.

Read Committed : 다른 세션에서 커밋된 데이터만 읽을 수 있음

Repeatable Read : 같은 트랜잭션에서 같은 데이터를 반복해서 읽을 때 항상 동일한 결과를 보장.

Serializable : 가장 높은 수준의 격리. 트랜잭션을 직렬화 된 순서로 실행하는 것처럼 보임.

 

트랜잭션 격리수준 차이점

트랜잭션 격리 수준 Dirty Read Non-Repeatable Read Phantom Read 특징
Read Uncommited 허용 허용 허용 다른 트랜잭션이 COMMIT하지 않은 데이터를 읽을 수 있음
Read Commited 차단 허용 허용 다른 트랜잭션이 COMMIT 한 데이터만 읽을 수 있음
Repeatable Read 차단 차단 허용 트랜잭션이 시작된 이후 동일 SELECT 결과를 항상 유지함 Phantom Read는 방지되지 않음.
Serializable 차단 차단 차단 가장 엄격한 격리수준. 트랜잭션 간 완전한 일관성 보장. Phantom Read 방지

 

Dirty Read

다른 트랜잭션이 COMMIT하지 않은 데이터를 읽음.

: 트랜잭션 A에서 데이터를 수정했지만 아직 COMMIT하지 않은 상태를 트랜잭션 B가 읽음.

 

Non-Repeatable Read

동일 트랜잭션 내에서 같은 데이터를 여러 번 읽을 때 값이 달라짐 (다른 트랜잭션의 COMMIT된 변경 때문).

:트랜잭션 A에서 데이터를 읽은 후, 트랜잭션 B가 값을 수정하고 COMMIT하면, 트랜잭션 A가 다시 읽을 때 값이 달라짐.

 

Phantom Read

동일 트랜잭션 내에서 같은 조건으로 데이터를 읽었을 때, 다른 트랜잭션의 INSERT로 인해 결과가 달라짐.

트랜잭션 A WHERE 조건으로 데이터를 읽은 후, 트랜잭션 B가 조건에 맞는 데이터를 INSERT하면, 트랜잭션 A가 다시 조회했을 때 새로운 행이 포함됨.

 

MVCC를 위한 스냅샷 저장관리 영역

DBMS 스냅샷 저장위치 관리방식 설명
Oracle Undo tablespace 관리 Undo 로그를 통해 관리 데이터를 변경하기 전에 Undo 로그를 생성하여 이전 값을 저장. 트랜잭션이 시작될 때 스냅샷을 통해 일관된 읽기 제공.
MySQL(InnoDB) Undo Logs Undo 로그와 트랜잭션 ID로 관리 InnoDB Undo 로그에 이전 데이터를 저장하고, 트랜잭션 ID Rollback Segment를 사용하여 스냅샷을 제공합니다.
PostgreSQL Table & WAL Vacuum과 트랜잭션 ID로 관리 각 테이블의 행에서 MVCC 데이터를 유지하며, Dead Tuple Vacuum 프로세스를 통해 정리. 트랜잭션 ID를 기반으로 일관된 스냅샷을 제공합니다.
MSSQL TempDB Snapshot Isolation TempDB 관리 Snapshot Isolation TempDB 관리 TempDB에 스냅샷 데이터를 저장하여 일관된 읽기 제공. Row Versioning 기능을 사용하여 스냅샷 기반 트랜잭션 구현.

 

 

(MySQL 기준 테스트) Read Uncommitted

READ Uncommitted 테스트
Dirty Read가 가능하며, 커밋되지 않은 변경 사항을 다른 트랜잭션에서 읽을 수 있습니다.
CREATE DATABASE IF NOT EXISTS mvcc;
USE mvcc;
-- 테스트 테이블 생성
CREATE TABLE read_uncommitted (
    id INT PRIMARY KEY,
    value VARCHAR(100)
);
 
-- 초기 데이터 삽입
INSERT INTO read_uncommitted VALUES (1, 'Initial Value');
COMMIT;
#Session1
-- 트랜잭션 시작
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
 
-- commit 업데이트 (커밋하지 않음)
UPDATE read_uncommitted SET value = 'Updated by Session 1' WHERE id = 1;
 
-- 업데이트된 값 확인
SELECT * FROM read_uncommitted WHERE id = 1;
-- 트랜잭션 유지
 

 
#Session2
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
 
-- Session 1에서 업데이트된 값 읽기 (Dirty Read 가능 여부 확인)
SELECT * FROM read_uncommitted WHERE id = 1;
 
-- 트랜잭션 종료
COMMIT;
 

 
Session 2에서 Session 1의 미완료 (Commit하지 않은) 데이터를 읽을 수 있음(Dirty Read).

 

(MySQL 기준 테스트) Repeatable_Read

Repeatable_Read한 트랜잭션에서 동일한 데이터를 여러 번 읽어도 같은 데이터 값을 얻을 수 있다. 중간에 다른 트랜잭션이 해당 데이터를 변경해도, 상관없이 이전에 읽었던 같은 데이터를 표시한다
-- 테스트 테이블 생성
CREATE TABLE Repeatable_Read (
    id INT PRIMARY KEY,
    value VARCHAR(100)
);
 
-- 초기 데이터 삽입
INSERT INTO Repeatable_Read VALUES (1, 'Initial Value');
COMMIT;
#Session1
-- 트랜잭션 시작
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
 
SELECT * FROM Repeatable_Read WHERE id = 1;
-- 트랜잭션 유지
-- 다른 세션에서 데이터 업데이트 시도
#Session2
 
-- 트랜잭션 시작
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
 
-- 데이터 업데이트 후 커밋
UPDATE Repeatable_Read SET value = 'Updated by Session 2' WHERE id = 1;
COMMIT;

#Session1
SELECT * FROM Repeatable_Read WHERE id = 1;
 


 
Session2에서 UPDATE COMMIT를 수행했지만 Session1에서 조회하게 될 경우 이전 값을 보여주게 된다.
 

(Oracle 기준 테스트) READ COMMITTED

READ COMMITTED 테스트
-- 테이블 생성
CREATE TABLE READ_COMMITTED (
    id NUMBER PRIMARY KEY,
    value VARCHAR2(100)
);
 
-- 데이터 삽입
INSERT INTO READ_COMMITTED  VALUES (1, 'Initial Value');
COMMIT;
#Session1
 
- 트랜잭션 시작 (명시적으로 COMMIT하지 않음)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
 
-- 현재 데이터 확인
SELECT * FROM READ_COMMITTED  WHERE id = 1;
-- 결과

 
-- 데이터 업데이트
UPDATE READ_COMMITTED  SET value = 'Updated in Session 1' WHERE id = 1;


-- 트랜잭션을 보류하여 다른 세션에서 상태 확인 가능
-- COMMIT하지 않고 대기
#Session2
 
-- 트랜잭션 시작
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
 
-- 데이터 읽기
SELECT * FROM READ_COMMITTED WHERE id = 1;
-- 결과: 'Initial Value' (세션 1의 변경 내용이 아직 커밋 되지 않았기 때문에 초기 값이 반환됨)

 
-- 데이터를 업데이트 시도 (락 충돌 발생 가능)
UPDATE READ_COMMITTED  SET value = 'Updated in Session 2' WHERE id = 1;

-- 결과: 세션 1 COMMIT 또는 ROLLBACK을 수행하기 전까지 대기

#Session1
세션1에서 COMMIT
COMMIT;

세션1 COMMIT하면 세션 2 UPDATE 작업이 진행됩니다.
이후 세션 2에서 데이터를 다시 읽으면 최신 값을 확인할 수 있습니다.
#Session2





 
아직 Session 2에서 commit 하지 않았기 때문에 현재 Session1에서는 Updated in Session 1 표시되며
LOCK 대기 상태는 풀리게 됩니다.
 

 

Oracle (기준 테스트) SERIALIZABLE

SERIALIZABLE 테스트
SERIALIZABLE 격리 수준에서는 트랜잭션이 직렬적으로 실행되는 것처럼 보장되며, 다른 트랜잭션이 데이터를 동시에 조회 수정하거나 삽입하려고 하면 대기하거나 충돌이 발생합니다.
 
session1
 
CREATE TABLE test_serializable (
    id NUMBER PRIMARY KEY,
    value VARCHAR2(100)
);
 
-- 초기 데이터 삽입
INSERT INTO test_serializable VALUES (1, 'Initial Value');
COMMIT;
 
-- SERIALIZABLE 설정
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
 
-- 데이터 읽기
SELECT * FROM test_serializable WHERE id = 1;


 
-- 데이터 업데이트
UPDATE test_serializable SET value = 'Updated in Session 1' WHERE id = 1;
 
-- 트랜잭션을 보류하여 다른 세션에서 상태 확인 가능
COMMIT;
 
-- 데이터 읽기
SELECT * FROM test_serializable WHERE id = 1;
-- COMMIT 하지 않고 대기
session2
-- SERIALIZABLE 설정
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
 
-- 데이터 읽기 시도
SELECT * FROM test_serializable WHERE id = 1;

 
-- 데이터 업데이트 시도
UPDATE test_serializable SET value = 'Updated in Session 2' WHERE id = 1;


2 Session LOCK 충돌로 인해 대기 상태


 
session1
-- 트랜잭션 커밋
COMMIT;

 
session2
-- 바로 대기 상태 해제,

-- 대기 상태는 빠지지만 격리수준 설정 때문에 에러가 발생하게 됩니다.
 
Serializable 격리 수준은 트랜잭션이 마치 시스템에서 단독으로 실행되는 것처럼 보장하기 위해 사용 그러나 동시에 실행 중인 다른 트랜잭션이 같은 데이터를 수정하거나 잠그는 경우, 충돌이 발생
 
 
 
Session1 종료
Session2
SELECT * FROM test_serializable WHERE id = 1;
-- 이전 SESSION1에서 초기 데이터를 보여주게 됩니다.
commit
SELECT * FROM test_serializable WHERE id = 1;
-- 이전 SESSION1에서 업데이트 데이터를 보여주게 됩니다.
 



 
Update 트랜잭션 실패 후 Session2에서 commit 하게 되면 Session1에서 마지막으로 수행한 업데이트 내용만 반영됩니다.
 
Serializable 모드는 높은 데이터 무결성을 보장하지만 성능에 영향을 미칠 수 있습니다. 따라서 이 격리 수준이 필요한 경우에만 사용하고, 충돌 가능성을 설계 단계에서 미리 고려해야 합니다.

일관성 과 동시성의 성능 상관관계

정리

1.동시성을 중심으로 설계할 경우
 
장점:
 
더 많은 트랜잭션을 동시에 처리할 수 있어 성능에 유리
지연이 줄어들고 시스템의 처리량이 증가합니다.
 
단점:
 
동시 실행 중인 트랜잭션 간의 충돌이 발생할 가능성이 커지게 된다.
Dirty Read, Non-Repeatable Read, Phantom Read와 같은 문제가 발생할 수 있어 데이터 일관성이 낮아질 위험이 있습니다.
 
: 금융 애플리케이션에서는 부적합하지만, 로그 데이터 기록이나 분석 워크로드와 같이 약간의 데이터 불일치가 큰 문제가 되지 않는 경우 적합
 
2. 일관성을 중심으로 설계할 경우
 
장점:
트랜잭션 간의 철저한 격리를 통해 데이터 무결성과 일관성을 보장
동시 작업 중에도 데이터에 대한 신뢰도가 높아지게 됨
단점:
동시 처리 가능 트랜잭션 수가 줄어들고, 시스템의 처리량이 감소
잠금(lock) 또는 대기(waiting)가 자주 발생하여 성능에 영향을 미치게 된다.
 
: 은행, 회계 시스템과 같이 데이터 정확성과 신뢰성이 필수적인 경우에 적합합니다.
 
결론: 동시성을 강조하면 성능이 좋아지지만 데이터의 일관성을 보장하기 어렵습니다.
일관성을 강조하면 성능이 다소 낮아질 수 있지만 데이터의 무결성과 신뢰성을 보장할 수 있다.
애플리케이션의 요구사항에 따라 격리 수준을 적절히 선택하는 것이 중요
 
동시성 중점: Read Uncommitted 또는 Read Committed
일관성 중점: Repeatable Read 또는 Serializable
반응형