Contents

트랜잭션

트랜잭션은 데이터베이스 상태를 변환시키는 하나의 논리적 기능 수행 단위이다. 즉, 한 번에 처리되어야 할 일련의 작업을 말한다.

트랜잭션으로 원하는 작업의 단위를 정하게 되면 작업을 수행하는 도중 문제가 발생하였을 때 이전에 진행되었던 작업이 롤백된다. 모든 작업을 안정적으로 끝마친 후에 실제 데이터베이스에 반영할 수 있다.

트랜잭션의 범위는 커넥션 기준이다. 스프링 프레임워크의 트랜잭션 처리는 메소드 간에 커넥션 객체를 전달하지 않아도 한 트랜잭션으로 묶어서 실행하도록 돕는다.

트랜잭션의 성질

Atomicity(원자성)

하나의 트랜잭션이 더 작게 나눌 수 없는 최소의 단위라는 뜻이다. 트랜잭션이 모두 반영되거나, 아니면 전혀 반영되지 않아야 하는 특징을 나타낸다. 계좌이체를 하는 경우를 생각해보자. 송금하는 도중에 문제가 발생하여 돈을 받아야 하는 사람에게 제대로 전달되지 않았다. 하지만 돈을 보내는 사람에게서는 이미 돈이 빠져나갔다면 큰 문제가 발생하게 될 것이다. 계좌이체 도중 문제가 발생하게 되면 송금하기 이전상태를 유지하게 되는 것을 원자성이라 볼 수 있다.

Consistency(일관성)

트랜잭션이 완료된 결과값이 일관적인 데이터베이스 상태를 유지하는 것을 말한다. 사실 이 말만으로는 잘 이해가 되지 않는다. 예를 들면, 고객 정보가 담겨있는 데이터베이스에 새로운 고객이 등록되면 그 데이터베이스를 참조하는 다른 하위 계층의 데이터베이스도 같은 고객의 세부 정보를 가져와야 한다. 또는 모든 계좌의 돈은 0원 이상이어야 한다. 라는 무결성 제약이 있다면 이를 위반하는 트랜잭션은 모두 중단되어야 한다.

Isolation(독립성)

트랜잭션이 수행되고 있을 때, 다른 트랜잭션의 연산작업이 중간에 끼어들어 기존 작업에 영향을 주지 못하도록 하는 것을 말한다. 독립성이 보장된다면 계좌 이체작업을 진행하고 있는 도중에 계좌의 잔액을 조회한다 거나 하는 작업을 동시에 수행할 수 없게 되는 것이다.

Durability(지속성)

트랜잭션을 성공한 후 데이터베이스에 반영된 것은 영원히 반영되어야 한다는 것을 의미한다. 시스템에 문제가 발생하거나 종료되더라도 데이터베이스에 반영된 값은 그대로 유지되어야 한다. 은행에 시스템이 마비되었다가 재개되었을 때 계좌의 금액이 바뀌게 된다면 큰 문제가 될 것이다.

트랜잭션 격리 수준

여러 클라이언트가 같은 데이터에 접근할 때 문제가 발생할 수 있다. 이러한 상황을 경쟁상태(Race Condition)라고 한다. 이 때 트랜잭션을 격리해서 다른 트랜재션이 영향을 주지 못하게 해야한다.

Isolation Level 설명 Dirty Read Non-Repeatable Read Phantom Read 고립성(안정성) 동시성
Read Uncommitted 커밋되지 않은 데이터도 읽을 수 있다. O O O 낮다 높다
Read Committed 커밋된 정보만 읽는다. X O O 중간 중간
Repeatable Read 하나의 트랜잭션에서는 하나의 스냅샷만 사용한다. X X O 중간 중간
Serializable Read 시에 DML 작업이 동시에 진행될 수 없다. X X X 높다 낮다

READ UNCOMMITTED

  • 각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK 여부에 상관 없이 다른 트랜잭션에서 값을 읽을 수 있다.(Dirty Read)
  • 정합성에 문제가 많은 격리 수준이기 때문에 사용하지 않는 것을 권장한다.
Dirty Read
commit되지 않은 정보를 볼 수 있는 현상을 말하는 것으로 Read Uncommitted Isolation Level일 때 발생하는 Read 현상이다.

READ COMMITTED

  • 커밋되지 않은 데이터 읽기 (dirty read), 커밋되지 않은 데이터 덮어쓰기 (dirty write) 문제를 해결한다.
  • 커밋된 데이터만 읽기: 다른 트랜잭션이 쓰고 있으면 대기 했다가 읽게 됨
  • 커밋된 데이터만 덮어쓰기 : 같은 데이터를 수정한 트랜잭션이 끝날 때까지 대기
  • 하나의 트랜잭션에서 같은 쿼리를 두번 실행하여 데이터를 조회할때 값이 달라질 수 있다.(Non-Repeatable Read) 커밋된 정보를 어떤 제약 없이 읽을 수 있기 때문에 여러 스냅샷 버젼으로 읽는 것이 가능하기 때문이다.
Non-Repeatable Read
한 트랜잭션안에서 같은 쿼리를 두번 실행 했을 때, 다른 값이 나오는 Read 현상을 말하는 것으로, 하나의 트랜잭션안에서 여러 스냅샷이 사용되는 경우를 말한다. Read-Committed 이하의 Isolation Level에서 나오는 현상이다.

REPEATABLE READ

  • 다른 사용자가 커밋 이후에 데이터를 조회할 경우 데이터가 변경되는 문제(read skew)를 해결한다.
  • Undo 공간에 백업해두고 실제 레코드 값을 변경한다.
  • 백업된 데이터는 불필요하다고 판단하는 시점에 주기적으로 삭제한다.
  • Undo에 백업된 레코드가 많아지면 MySQL 서버의 처리 성능이 떨어질 수 있다.
  • 하나의 트랜잭션 안에서 하나의 스냅샨 버젼으로만 동작하기 때문에 같은 쿼리를 두번 실행한다 할지라도 동일한 값이 유지된다.
  • SQL 표준 트랜잭션 격리 수준에 따르면 Phantom Read를 막지 못한다. 그러나 MySQL InnoDB 엔진의 경우 consistent read를 사용하기 때문에 REPEATABLE READ 레벨임에도 불구하고 phantom read가 발생하지 않는다.
Phantom Read
트랜잭션 안에서 첫번째 쿼리 수행 결과와 두번째 쿼리 수행 결과가 다른것을 나타낸다. 영향을 받는 행 자체가 업데이트되지 않았더라도 행이 추가되거나 삭제된 경우 다른 결과를 반환한다.

SERIALIZABLE

  • 한 트랜잭션의 결과가 다른 트랜잭션의 퀴리 결과에 영향을 주는 문제(같은 데이터를 쓰지 않지만 실제로는 경쟁 상태)를 해결한다.
  • 인덱스 잠금이나 조건 기반 잠금 등을 사용한다.
  • 가장 단순한 격리 수준이지만 가장 엄격한 격리 수준이다.
  • 성능 측면에서는 동시 처리성능이 가장 낮다.
  • 데이터의 안정성에 위배하는 어떤 것도 발생할 수 없을 정도로 높은 고립성을 가지고 있다. 하지만, 그로 인해 동시성이 많이 떨어진다. 그래서, 하나의 데이터영역에 READ가 발생할 때 어떤 DML도 발생할 수 없어서 많은 문제점을 야기시킨다.

각 DBMS의 기본 트랜잭션 격리수준

대부분의 RDBMS는 Read Committed나 Repeatable Read로 고립성을 유지하여 트랜잭션을 지원한다.

  • mysql : Repeable-read
  • postgres : Read Committed
  • oracle : Read Committed
  • mongodb : Read uncommitted

참고

https://www.youtube.com/watch?v=poyjLx-LOEU

https://happy-coding-day.tistory.com/190

https://mysqldba.tistory.com/334