소프트웨어-이야기/프로그래밍 언어와 프레임워크

데이터베이스 트랜잭션과 격리수준

americano_people 2024. 12. 27. 15:52

Transaction

트랜잭션은 작업의 완전성을 보장해주는 것을 말한다.

  1. 논리적인 작업 셋을 모두 완벽하게 처리한다. --> 트랜잭션의 Commit 작업을 뜻한다.
  2. 처리하지 못할 경우에는 원 상태로 복구한다. --> 트랜잭션의 Rollback 작업을 뜻한다.

이렇게 작업의 완전성을 보장하여, partial update 문제를 방지한다.

(a) partial update 문제

partial update은 작업이 일부만 적용되는 현상이다.

myisam 엔진과 Innodb 엔진 사례를 통해, 트랜잭션이 적용되지 않아 "partial update" 문제가 발생하는 사례를 살펴보자. (myisam 엔진은 트랜잭션을 지원하지 않는다.)

위의 그림은 PK가 중복되는 레코드를 추가하여 오류가 발생했을 때, 엔진 별로 최종 데이터 결과가 다름을 보여준다.

PK 중복 오류 발생 시, innodb는 트랜잭션에 의하여 롤백된다. 그래서 명령문 전체가 롤백된다. 그러나 myisam 엔진은 오류가 발생한 데이터만 반영되지 않을 뿐, 이전에 실행했던 명령문은 롤백되지 않는다.

이처럼 데이터가 부분적으로 반영되는 경우, 남은 찌꺼기 레코드를 애플리케이션에서 직접 지워주는 작업이 필요하다.

(b) Lock (잠금)

트랜잭션과 잠금은 비슷해보이지만 다르다.

  • 트랜잭션: 데이터의 정합성을 보장하기 위한 기능이다.
  • 잠금: 동시성을 제어하기 위한 기능이다.

잠금은 여러 커넥션에서 동시에 동일한 자원을 요청할 경우, 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 역할을 한다. 만약, 하나의 데이터를 여러 커넥션에서 동시에 변경할 수 있게 되면, 레코드의 값을 예측할 수 없게 된다.

잠금은 동시성 문제와 잠금에 따로 정리했다. 

데이터 정합성 문제 

(1) Dirty Read

어떤 트랜잭션에서 처리한 작업이 완료되지 않았은데도, 다른 트랜잭션에서 볼 수 있게 되는 현상을 말한다. 커밋되지 않았던 데이터를 다른 트랜잭션에서 사용하게 되면, 애플리케이션 개발이 혼란스럽게 된다. 그래서 DirtyRead를 발생시킬 수 있는 READ UNCOMMITED 격리 수준은 사용을 권장하지 않는다.

쇼핑몰의 쿠폰다운로드 사례를 통해, Dirty Read가 발생하면 생기는 문제를 살펴보자. 

(2) Non-Repetable Read 

"REPETABLE READ"는 한 트랜잭션 내에서 똑같은 SELECT 명령어를 실행했을 때, 항상 같은 결과를 가져와야한다는 정합성을 말한다.  "NON-REPETABLE READ"은 이 정합성을 지키지 못하는 상황을 말한다.

주식 체결 사례를 통해, "NON-REPETABLE READ" 문제를 살펴보자. 

(3) Phantom Read (유령읽기)

한 트랜잭션에서 특정 조건으로 조회했을 때, 다른 트랜잭션에서 추가/삭제한 레코드가 조회되는 현상을 말한다. REPETABLE READ 격리 수준은 새로운 레코드 추가/삭제 데이터를 막지 못한다. 

일반적인 데이터베이스에서는 REPEATABLE READ 격리 수준에서 Phantom Read 문제가 발생할 수 있다. 예를 들어 PostgreSql이 그러하다.

반면, MySQL은 REPEATABLE READ 격리 수준에서도 Phantom Read를 방지한다. 내부에서 gap lock을 사용하여 문제를 해결한다. 그러나 "SELECT … FOR UPDATE" 질의로 잠금을 거는 경우, Phantom Read 현상이 발생할 수도 있다. 그러나 이 경우는 거의 드물다. 

Transaction 격리 수준

하나의 트랜잭션 내에서 작업 내용을 어떻게 공유하고 차단할 것인지를 결정하는 레벨을 말한다.

격리 수준 Dirty Read  Non-Repeatable Read  Phantom Read  Write Skew
READ UNCOMMITTED O O O X
READ COMMITTED X O O X
REPEATABLE READ X X
(MySQL > InnoDB에서는 거의 발생 X)
X
SERIALIZABLE X X X O

  • READ UNCOMMITTED
    • 거의 사용하지 않는 격리수준이다. 커밋되지 않은 데이터를 읽어서, Dirty Read 문제를 야기할 수 있다.
  • READ COMMITTED 
    • Oracle, PostgreSQL의 기본 격리 수준이다.
  • REPEATABLE READ
    • MySQL의 기본 격리 수준이다.
    • 격리 수준이 높아질 수록 MySQL의 서버 처리 성능이 떨어질 것 같지만, SERIALIZABLE이 아니면 크게 성능의 개선이나 저하는 발생하지 않는다.
  • SERIALIZABLE
    • SERIALIZABLE은 트랜잭션이 논리적으로 순차적으로 실행되도록 보장한다. 
    • 인덱스 잠금 혹은 조건 기반 잠금 등을 사용하여 구현한다. 
    • 동시성이 중요한 데이터베이스에서는 거의 사용하지 않는다.

 

참고