DB 트랜잭션!

트랜잭션

트랜잭션은 작업의 완전성을 보장한다.

논리적인 작업 셋을 모두 완벽하게 처리하거나, 작업의 일부만 적용되는 현상(Partial update)이 발생하지 않는것을 보장하는 기능이다.

트랜잭션을 상당히 골치아픈 기능으로 생각하지만, 그만큼 애플리케이션 개발에서 고민해야 할 문제를 줄여주는 필수적 DBMS 기능이다.

계좌시스템을 예로 들면 A가 B에게 입금하는 작업을 하나의 트랙잰석(작업단위)로 볼 수 있다.
아래 2작업이 한번에 이루어져야 한다.

1) A가 B에게 돈을 입금하고 A의 보유금을 마이너스
2) B는 A에게 입금받은 돈은 보유금에서 플러스

만약 1번만 성공하고 2번은 실패할 경우 A 계좌에서 돈만 빠져나가는 오류가 발생하게 된다.

트랜잭션이 성공하면 commit 하고 중간에 어떠한 과정이라도 실패한게 있다면 rollback해야 한다.

DML문을 실행하면 해당 트랜젝션에 의해 발생한 데이터가 다른 사용자에 의해 변경이 발생하지 않도록 lock을 발생시킨다.

lockcommit 또는 rollback 문이 실행되면 해제된다.

오라클 SYS 계정과 SCOTT 계정에 각각 접속하여 트랜잭션 처리를 확인. SYS계정에선 MILLER의 job이 CLERK라 출력된다.

-- SCOTT의 계정에서 실행
UPDATE emp SET job = 'ARTIST' WHERE ename = 'MILLER';

SELECT ename, job FROM emp WHERE ename = 'MILLER'; 
-- (MILLER, ARTIST) 출력
-- SYS의 계정에서 실행
SELECT ename, job FROM emp WHERE ename = 'MILLER';
-- (MILLER, CLERK) 출력

image1

SYS가 빨간색, SCOTT이 파란색.

SCOTT 계정 접속세션에서만 변경되었기 때문에 SYS 계정 세션에선 MILLER의 job은 아직 CLERK이다.

이 상태에서 SYS계정에서 MILLER의 job을 XXXX로 업데이트 진행.

UPDATE scott.emp
SET job = 'XXXXX'
WHERE ename = 'MILLER';

아직 SCOTT 계정 세션에서 update & commit 하지 않았기 때문에 SYS 계정 세션에선 lock 으로 인해 무한로딩된다.

image1

SCOTT 계정세션에서 commit 하는 순간 SYS 계졍세션의 updatelock 이 풀리면서 실행된다.

image1

이젠 SYS 계정 세션에서 emp 테이블에 lock 을 걸었고 commit 해주기 전엔 다른 세션에서 해당 데이터를 수정하거나 삭제하지 못한다.

ACID

출처: 위키

ACID(원자성, 일관성, 고립성, 지속성) 는 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질을 가리키는 약어이다.

원자성(Atomicity)
트랜잭션 내 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는, 실패하더라도 모든 내용을 취소하는 어보트 능력(abortability) 이다.

일관성(Consistency)
트랜잭션이 성공적으로 완료하면 설정해둔 무결성 제약 을 포함해 언제나 일관성 있는 데이터베이스 상태로 유지하는 것을 의미한다.

격리성(Isolation)
트랜잭션A 수행 시 다른 트랜잭션B 가 끼어들지 못하도록 보장하는 것을 의미한다.

지속성(Durability)
트랜잭션이 성공적으로 완료하면 영원히 반영되어야 함을 의미한다. 복구/복제 기능을 구현하기 위해 여러가지 기능이 트랜잭션 로그를 사용함.

스케줄 serializable

자세한 설명: https://www.youtube.com/watch?v=DwRN24nWbEc

Serializable 여러개의 트랜잭션이 동일한 시간대에 실행될 경우 read/write 요청이 섞이더라도 모두 동일한 결과값을 요하는 성질이다.

DB 가 트랜잭션을 받게되면 아래 2가지 방식으로 수행될 수 있다.

  • Serial Shcedule
  • Non-serial Schedule

image1

모든 트랜잭션이 Serial shcedule 으로 처리된다면 아래에서 알아볼 [락, 격리] 등은 알필요가 없다.

하지만 대부분 DBMS 에선 성능때문에 트랜잭션을 Non-serial Schedule 으로 처리하면서
ACID 요구사항을 만족시키기 위해 각종 제약조건을 사용한다.

제약조건의 목표는 Non-serial Schedule 의 트랜잭션들이 conflict serializable(충돌직렬화) 조건을 만족하는 것.

conflict serializableNon-serial Schedule 의 트랜잭션들이 하나의 자원을 read/write 하는 순서가
Serial shcedule 에서 하나의 자원을 read/write 하는 순서가 동일함을 뜻한다.

read/write 하는 순서가 동일할 경우 Serial Schedule Non-serial Schedule 상관없이 모두 동일한 결과가 출력된다.

스케줄 recoverability

recoverability 는 스케줄이 rollback 되었을 때 복원성을 의미한다.

여러개의 트랜잭션이 동시에 실행되다 특정 트랜잭션이 rollback 될 경우 정상적으로 복원되지 않을 경우가 있다.

특이한 조건에서 recoverability 가 손상되는 상황을 알아보고
아래 3가지 종류의 스케줄을 사용해 방지하는 방법을 알아본다.

  • Recoverability Schedule
  • Cascadeless Schedule
  • Strict Schedule

https://www.geeksforgeeks.org/types-of-schedules-based-recoverability-in-dbms/

Recoverability Schedule

아래 그림처럼 스케줄 내에서 Dirty Read 가 발생하는 경우를 복구하기 위한 스케줄 방법임.

image1

T2 가 commit 되지 않은 값을 read, R(A)=6 하고 T1 이 rollback 하게되면서 부정확한 값이 되어버렸다.

트랜잭션의 의존관계를 파악하고, 부모 트랜잭션이 commit/rollback 되는것을 확인한 후 자식 트랜잭션도 commit/rollback 하는 스케줄을 Recoverability Schedule(보존 스케줄) 이라 한다.

Recoverability Schedule 은 트랜잭션의 의존관계를 파악하고 부모 트랜잭션과 자식 트랜잭션을 연결해서 commit/rollback 해야한다. cacading rollback 이라 하는데 비용이 높아 사용하지 않는다.

그래서 cacading rollback 기술을 사용하지 않으면서도 recoverability 를 확보하기 위해 Cascadeless Schedule 를 사용한다.

Cascadeless Schedule

데이터를 write 한 트랜잭션이 commit/rollback 되기 전까지는 read 를 제한하는 스케줄을 Cascadeless Schedule(비종속 스케줄) 이라 한다.

아래 그림처럼 동일한 자원 X 를 건드리는 트랜잭션 3개가 있을 경우 commit(초록박스) 되기까지는 read 접근을 막아 종속관계를 없엔다.

image1

Cascadeless Scheduleread 에 대해서만 접근제한하고 wirte 는 별도로 제한하지 않기 때문에,
write 트랜잭션이 연속적으로 일어나는 환경에서 rollback 으로 인해 일부 write 트랜잭션들이 무시될 수 있다.

Strict Schedule

데이터를 write 한 트랜잭션이 commit/rollback 되기 전까지는 read/write 모두 제한하는 스케줄을 Strict Schedule(엄격한 스케줄) 이라 한다.

아래 그림과 같이 스케줄 다이어그램으로 구분할 수 있으며
Strict Schedule 이 공유자원에 대해 가장 엄격하게 관리한다.

image1

앞으로 설명할 [락, 격리레벨] 등은 모두 트랜잭션의 [serializable, recoverability] 속성을 만족시키기 위해서다.

락(Lock)

락은 동시성을 제어하기 위한 기능
트랜잭션은 데이터의 정합성을 보장하기 위한 기능.

락은 개념적으로는 아래와 같이 두종류로 나눌 수 있다.

  • Read Lock
    Shared Lock(공유 락) 이라고도 하며 다른 트랜잭션이 read 하는것을 허용하고 write 하는것을 비허용한다.
  • Write Lock
    Exclusive Lock(배타적 락) 이라고도 하며 다른 트랜잭션이 read/write 하는것을 모두 비허용한다.

락의 호환성은 아래와 같다.

  Read Lock Write Lock
Read Lock O X
Write Lock X X

MySQL InnoDB Lock

MySQL 에서 기본적으로 제공하는 잠금은 아래와 같다.

  • Global Lock: MySQL 서버 전체 잠금을 설정, 백업시에 주로 사용.
  • Table Lock: 개별 테이블 단위로 잠금을 설정
  • Named Lock: GET_LOCK() 함수를 통해 임의 문자열에 대해 잠금을 설정.

MySQL InnoDB 에서 별도로 레코드 기반 잠금기능을 제공한다.

  • Record Lock: 인덱스의 레코드를 잠근다. 기본키, 유니크 인덱스 관련 작업은 Record Lock 을 사용한다.
  • Gap Lock: 레코드와 인접한 위 아래 간격을 잠근다. 레코드 사이 새로운 INSERT 를 제어한다.
  • Next-Key Lock: Record Lock + Gap Lock 형태의 잠금.

image1

MySQL InnoDB 에서 인덱스 기반으로 SELECT 요청을 할 경우 해당 인덱스의 레코드에 Next-Key Lock 이 발생한다.

해당 인덱스를 수정/삭제 하거나, Gap 안에 새로운 레코드를 추가할 수 없다.

만약 인덱스가 아닌 필드를 기반으로 SELECT 요청을 할 경우 테이블 전체에 락을 걸게 됨으로 철저한 인덱스 설계가 필요하다.

트랜잭션 격리

트랜잭션 격리 는 트랜잭션 처리 과정에서 두개 이상의 트랜젝션이 동시에 같은 공유 자원 접근시(Race Condition) 발생하는 문제를 처리하기 위한 제약조건이다.

위에서 설명한 트랜잭션의 [serializable, recoverability] 속성을 만족시키기 위한 제약조건이기도 하다.

여러개의 트랜잭션이 하나의 자원에 접근할 때 별도의 제약조건 없이 read/write 할 수 있도록 풀어줄경우 아래와 같은 이상한 상황이 발생할 수 있다.

  • Dirty Read
  • Nonrepeatable Read
  • Phantom Read

Dirty Read

image1

READ_UNCOMMITED 가능할 경우 commit 되기전 정보를 read 할 수 있다.
이 과정에서 오류가 발생되어 읽어온 정보가 rollback 된다면 read 된 정보는 잘못된 정보가 되어버린다.

이렇게 잘못된 정보를 읽는 것을 Dirty read 라 한다.

READ_COMMITTED 으록 설정하면 commit 된 데이터만 read 할 수 있음으로 Dirty Read 가 발생하지 않는다.

Nonrepeatable Read

image1

한 트랜잭션에서 별도의 쓰기작업 없이 처음 읽어드린 값과 두번째 읽어드린 값이 다른 상황을 Nonrepeatable Read 이라한다.

Phantom Read

image1

여러개의 레코드를 read 하는 경우에서 발생하는 이슈로 처음 읽었을 때 와의 레코드개수와 두번째 읽었을 때 레코드 개수가 다른 경우를 Phantom Read 라 한다.

격리레벨(isolation level)

위와 같은 문제를 해결하기 위해 하나의 트랜잭션을 격리하기 위한 제약조건을 level 로 표현했다.

격리레벨 Dirty Read Nonrepeatable Read Phantom Read
READ_UNCOMMITED O O O
READ_COMMITTED X O O
REPEATABLE_READ X X O
SERIALIZABLE X X X
  • READ_UNCOMMITED
    다른 트랜잭션에서 커밋하지 않은 데이터 read 가능.
  • READ_COMMITTED
    다른 트랜잭션에 의해 커밋된 데이터만 read 가능.
  • REPEATABLE_READ
    DB 별로 구현방법이 다름, 스냅샷과 같은 방식으로 트랜잭션들을 격리함.
  • SERIALIZABLE
    선점한 트랜잭션이 수행줄일 때 write 할 수 없다.

높은 레벨일수록 고립도가 높아지며 성능이 떨어진다.
어플리케이션 형태에 따라 격리레벨을 설정하여 최적화 해야한다.

PostgreSQL 을 포함한 대부분 DBMS 는 기본적으로 READ_COMMITTED 을 사용한다.
MySQL 은 기본적으로 MVCC 기법을 사용한 REPEATABLE_READ 을 사용한다.
MySQL InnoDB 에선 SELECTNext-Key Lock 으로 인해 REPEATABLE_READ 격리레벨에서도 Phantom Read 가 발생하지 않는다.

추가이상현상

위에서는 3가지 이상현상인 [Dirty Read, Nonrepeatable Read, Phantom Read] 만 소개했는데
사실 3가지 이외에도 격리레벨마다 추가이상현상이 존재한다.

사실상 위 3가지 이상현상에 의해 파생되는 추가이상현이다.

READ_UNCOMMITED 추가이상현상 - Diirt Write
commit 되지 않는 데이터를 write 할 때 발생한다.
연달아서 write 만 하는 트랜잭션이 트랜잭션 스케줄이 있을 경우, 첫번째 트랜잰셕이 rollback 되면서 두번째 트랜잭션의 반영사항이 무시될 수 있다.

READ_COMMITTED 추가이상현상 - Lost Update
Shared Lock 이 없을 때 두번째 commit 이 첫번째 commit 을 덮어 씌우는 경우이다.
Dirty Read 한 후 해당 데이터로 write 하고 commit 하는 것이다.

이 외에도 read skew, write skew 가 존재하는데, 어플리케이션 레벨에서 값에 대한 제약조건을 if 로 구성해 놔도 Dirty Read 통과되어 버리는 오류이다.

MVCC

출처: Real MySQL

락 기반 Concurrency Control 의 경우 Read Lock 만 공유되기에 성능에서 떨어진다.
특히 [READ_COMMITTED, REPEATABLE_READ] 를 락 기반으로 구현할 경우 다량의 Read Lock 이 발생하고 심한 고립성을 가지게 된다.

그래서 대부분 DBMS 가 락 기반 Concurrency Control 을 하지 않고 MVCC(Multiversion Concurrency Control) 기법을 사용해 REPEATABLE_READ 를 구현한다.
DBMS 별로 다양한 MVCC 구현방법이 있는데, MySQL InnoDBMVCC 는 특정 시점의 변경정보를 version 으로 기록하기 위해 Undo Log 를 사용한다.

아래 그림과 같이 업데이트 이전 값은 Undo Log 에 빼놓고 새로 삽입한 값은 Buffer Pool 에 일정시간 저장되어 있다가 백그라운드 스레드에 의해 디스크에 기록된다.

image1

READ_UNCOMMITTED 의 경우 Buffer Pool 에서 데이터를 읽어서 반환하고
READ_COMMITTED 혹은 그 이상의 격리레벨의 경우 Undo Log 에서 데이터를 읽어 반환한다.

Undo Log 에서 락 제한 없이 값을 읽어 Non-Locking Consistent Read(잠금 없는 일관된 읽기) 라 부른다.

아래 그림처럼 4번 index rowBuffer PoolUndo Log 2가지 version 으로 관리된다.

image1

MySQL MVCC 문제점

Undo Log 를 사용해서 Non-Locking Consistent Read 하는 것은 좋지만, read 에 대해 제약조건 없이 접근을 허용할 경우 Dirty Read 가 발생할 수 밖에 없다.

위 그림만 봐도 실제 값은 경기 인데 읽어드린 값은 서울 이다.

여기서의 Dirty Read 는 위에서 설명한 rollback 으로 인한 Dirty Read 와는 결이 다르다.

순서 차이일 뿐 Undo Log 로부터 정상적으로 read 했기 때문에 Dirty Read 가 아니라고 볼수 있지만,
대다수의 아티클에서 해당 상황도 오류를 야기함으로 Dirty Read 에 포함시켜야 한다고 주장하고 있다.

만약 해당 방식으로 Dirty Read 한 값을 기반으로 write 를 수행하면 Lost Update 까지 이어진다.

물론 여기서 말하는 Lost Update 는 2번의 트랜잭션으로 이루어지는 어플리케이션 코드에서 발생하는 오류이다.

MySQL MVCC 에서 기본 격리레벨인 REPEATABLE_READ 는 별도의 락 없이 동작하기 때문에 Dirty Read - Lost Update 가 충분히 발생할 수 있다.

대부분 코드에서 read-modify-write 주기로 DB 에 접근하는 방식으로 문제를 해결하는 경우가 많아 Lost Update 는 자주 발생하는 이슈이다.

PostgreSQL MVCC 문제점

[Account1, Account2] Entityupdate 요청MVCC 로 처리하는 과정.
txid=12select 요청 진행, txid=13update 요청 진행한다.
row 의 write 요청이 일어날 때마다 새로운 version 의 복사본이 생성된다.

image1

  • created_by: row 를 삽입한 txid
  • deleted_by: row 를 삭제한 txid

txid=13deleted_by 만 설정할 뿐, 기존 value 는 건드리지 않고 업데이트 사항을 새로 추가한 복사본 version 을 생성한다.

나중에 모든 트랜잭션이 commit 되면 GC 가 deleted_by 표시된 row 들을 삭제한다.

deleted_by 가 활성화 되어 있어도 commit 되지 않았다면 읽기를 실행한다.
PostgreSQLMVCC 에서도 별도의 Read Lock 을 사용하지 않기 때문에 위에서 설명한 Dirty Read - Lost Update 가 발생할 수 있다.

스냅샷 격리(REPEATABLE_READ)

DB 벤더별로 다르지만 [PostgreSQL, MySQL] 에선 MVCC 기반의 REPEATABLE_READ스냅샷 격리 라고 부른다.

트랜잭션 수행을 형상으로 관리하고 commit 시점에 관리하던 형상을 한번에 뭉쳐 write 명령을 DB 에 전달해서 스냅샷 격리 라 부른다.

REPEATABLE_READ 에서 두개의 write 요청 트랜잭션에서 쓰기 충돌 을 제어 하기위한 여러가지 규칙이 있다.

  1. 트랜잭션이 abort 되면 그동안의 write 명령 은 모두 무시된다.
  2. 만약 트랜잭션의 start-commit 사이에 다른 트랜잭션이 먼저 commit 했다면 후에 commit 되는 트랜잭션은 abort 된다. 그래서 first-commiter win 이라는 이름으로 불리기도 한다.
  3. 자신보다 큰 트랜잭션ID 가 작성한 row 는 commit 여부 상관없이 질의해도 보이지 않는다.

스냅샷 격리 에서 색인은 모든 version 의 row 를 찾고 필터링 하는 방식으로 운영된다.

Lost Update 해결방안

Lost Update 발생원인은 read-modify-write 형태로 구성된 트랜잭션 때문이다.

Lost Update 를 해결하는 첫번째 방법은 원자적 쓰기연산 이다.

조회와 수정이 동시에 일어나는 write 요청 을 사용하여 로직을 처리할 수 있다면 가장 쉬운 해결책이다.

UPDATE counters SET value = value + 1 WHERE key = 'foo';

Lost Update 를 해결하는 두번째 방법은 명시적인 잠금 이다.

read/write 가 복잡하게 얽힌 비즈니스 로직과 높은 동시성 제어가 필요한 트랜잭션이라면 read 요청에도 베타적 설정을 지정해줘야 한다.

-- Locking Read
SELECT balance FROM account WHERE id = '1' FOR UPDATE; -- WRITE LOCK

이런 형태의 락을 Locking Read 라 하고 read 요청 이지만 Write Lock 을 요구한다.
동시에 실행되는 두 트랜잭션을 Locking Read 로 수행할 경우 read 요청 동시에 실행되지 못하도록 막기 때문에 Dirty Read 가 발생하지 않는다.

사실상 베타적락 기반 으로 실행되는 Concurrency Control 이랑 다름없다.
경쟁 조건이 발생할 수 있지만 동시에 발생하는 트랜잭션의 read-modify-write 가 순서대로 처리되도록 강제한다.

Lost Update 를 해결하는 세번째 방법은 갱신손실 자동감지 이다.

MySQL 에서 지원하지 않음.

DB에서 자체적으로 운영하는 트랜잭션 관리자read-modify-write 과정에서 Lost Update 를 발견하면 해당 트랜잭션을 abort 한다.
스냅숏 관리에서 가장 효과적으로 사용될 수 있는 방법이며 어플리케이션에서도 별도의 수정을 할 필요가 없다.

Lost Update 를 해결하는 네번째 방법은 Compare & Set 이다.

원자적 쓰기연산 과 유사한 방법으로 수정하며 이전값의 조건문을 통해 갱신을 시도한다.

UPDATE wiki_pages SET content = 'new content'
WHERE id = 1234 AND content = 'old content'; -- 이전 값 기준 필터링

Write Skew

Lost Update 가 일어나지 않는다면 모든 문제가 해결될것 같지만 그렇지 않다.
코드 구성에 따라 어플리케이션 레이어에서 제한하고 있는 조건을 위배할 수 있다.

경비 프로그램에서 최소 한명 이상의 경비원이 모니터링 하고 있고, 각 경비원은 전산에서 휴식버튼을 누르고 휴식할 수 있는 시스템에서 동시에 모든 경비원이 휴식버튼을 누르게 될경우 아마 모두 휴식상태로 값이 변경되며 어플리케이션에서 제한하고 있는 조건을 위배하게 될 것이다.

경비원 모두 자신의 레코드에 접근하여 status 값을 변경할 것이기 때문에 Lost Update, Dirty Read 로 발생한 문제도 아니다.

예약 시스템도 마찬가지이다, row 삽입을 통해 해당 시간대에 예약이 없는지 체크하고 예약 레코드를 삽입하는 시스템에서 동시에 동일한 예약시간에 예약을 진행하면 레코드가 2개가 들어가게 된다.

이런 이상 현상을 Write Skew 라 한다.
범위 질의를 통해 Phantom Read 가 발생한 이후 write 요청 시에 주로 발생한다.

SERIALIZABLE

충돌 구체화(materializing conflict)
충돌 구체화는 어플리케이션에서 데이터 모델을 인위적으로 비틀어 해결하는 방법이라 거의 사용하지 않는다.
위의 회의실 예약을 예로 들면, 예약 시간에 대한 row 를 만들고 row 집합에 대한 잠금 충돌로 Write Skew 를 예방하는 방법이다.

대부분 Write Skew 를 피하기 위해 SERIALIZABLE 격리수준을 사용한다.

SERIALIZABLE 격리수준은 발생 가능한 모든 경쟁 조건을 막는 가장 강력한 격리 수준이다.
대부분 DB 은 아래 3가지 기법중 하나를 사용한다.

  1. 트랜잭션을 순차실행
    단일코어, SP 기반으로 트랜잭션을 강제로 순서대로 실행되도록 제어한다.
  2. 2PL(two-phase locking)
  3. SERIALIZABLE SNAPSHOT

2PL(two-phase locking)

2PL(two-phase locking) 은 트랜잭션에서 모든 locking 명령이 최초의 unlock 명령보다 먼저 수행되도록 하는 방법이다.

락을 통해 트랜잭션의 read/write 순서를 강제하면서 자연스럽게 Serializable 를 만족하게 된다.
하지만 모든 트랜잭션이 자원을 공유하지 않기 위해 락을 흭득하게되면 데드락 상태가 발생하게 된다.

여기서 말하는 잠금의 종류는 아래와 같다.

  • 테이블 전체를 잠금
  • 색인 범위 잠금(index-range locking, next-key locking)
  • 서술 잠금(WHERE 조건을 통해 검색된 row 잠금)

Conservative 2PL

모든 Lock 을 취득한 다음 트랜잭션을 수행한다.
모든 자원을 가져야 트랜잭션이 수행됨으로 데드락이 발생하지 않는다.
하지만 모든 Lock 을 취득하기 위한 시간이 오래 걸림으로 성능이 떨어져 잘 사용하지 않는 방법이다.

read_lock(X)
write_lock(Y)
write_lock(Z)
...
unlock(X)
...
unlock(Y)
unlock(Z)
commit

Strict 2PL

Strict Schedule 을 보장하는 2PL.
Write Lockcommit/rollback 이후에 해제한다.

read_lock(X)
write_lock(Y)
write_lock(Z)
...
unlock(X)
...
commit
unlock(Y)
unlock(Z)

Strong Strict 2PL(SS2PL)

Strict Schedule 을 보장하는 2PL.
Write Lock, Read Lock 모두 commit/rollback 이후에 해제한다.

read_lock(X)
write_lock(Y)
write_lock(Z)
...
...
commit
unlock(X)
unlock(Y)
unlock(Z)

SERIALIZABLE SNAPSHOT

2008년에 처음 기술, 낙관적 동시성 제어기법이라 부르기도 함.
SERIALIZABLEMVCC(스냅숏) 을 적절히 섞어서 사용하는 방식이다.

SERIALIZABLE SNAPSHOT 은 잠금 없이 트랜잭션들을 실행시킨다.
실행중 격리가 되지 않은 트랜잭션은 abort 처리한다, 직렬적으로 실행된 트랜잭션은 commit 된다.

image1

트랜잭션이 직렬적으로 실행되었는지 여부는 MVCC 기반의 오래된 version row 를 읽었는지 여부로 판단한다.
트랜잭션이 commit 되기 전 해당 트랜잭션이 오래된 version row 를 읽었는지 한번 더 체크한다.

MVCC 기반으로 동작하는 점에서 트랜잭션 관리자의 갱신손실 자동감지 와는 다른 개념이라 볼 수 있다.

SERIALIZABLE SNAPSHOT 격리레벨에서 트랜잭션들의 경쟁이 심해지면 직렬로 실행되지 못한 트랜잭션이 많아지면서 abort 비율이 높아지고 되려 2PL 보다 못한 성능을 낼 수 있다.
또한 직렬화 여부를 온전히 DB 알고리즘에 의탁하는 과정에 부하가 일어날 수 있다.

그럼에도 SERIALIZABLE SNAPSHOT 를 사용해야 하는 이유는 락 없이 직렬화 구현이 가능하다는 점, 대부분 질의문이 read 요청 이기에 효율적이기 때문이다.

카테고리:

업데이트: