All Articles

트랜잭션과 격리수준

트랜잭션(Transaction)이란, 데이터베이스에 접근하여 수행하는 작업의 단위를 뜻한다. 데이터베이스에 접근은 SELECT, INSERT, UPDATE, DELETE 등 질의를 통해 이루어지는데, 이 각각의 질의가 아닌 하나 이상 질의의 묶음이 바로 트랜잭션이라는 점을 알아두자. 트랜잭션 설계를 잘하면 데이터를 다루는 것에 많은 이점이 있다.

트랜잭션의 4가지 특성 : ACID

  • 원자성(Atomicity) : 한 트랜잭션 내에서 실행한 작업들은 하나의 작업으로 간주한다. 즉, 모두 성공 또는 모두 실패되어야 하는 All or Nothing의 개념이다. (ex: 송금하는 사람의 계좌에서 돈은 빠져나갔는데 받는 사람의 계좌에 돈이 들어오지 않는 일은 없어야 한다.)
  • 일관성(Consistency) : 트랜잭션의 작업 처리가 완료되었다면 결과는 일관성이 유지되어야 한다. 참고로 일관성은 특정한 조건을 두고, 그 조건을 만족하는지를 확인하는 방식으로 검사할 수 있다. (ex: 송금하는 사람의 계좌 잔고가 0보다 작아지면 안된다.)
  • 격리성(Isolation) : 동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리 해야한다. (ex: 송금하는 사람의 계좌에서 돈은 빠져나갔는데 받는 사람의 계좌에 돈이 아직 들어가지 않은 DB 상황을 다른 transaction이 읽으면 안된다.)
  • 지속성(Durability) : 트랜잭션을 성공적으로 마치면 그 결과가 영구적으로 반영되어야 한다. (ex: 한 번 송금이 성공하면 은행 시스템에 장애가 발생하더라도 송금이 성공한 상태로 복구할 수 있어야 한다.)

이 중 격리성은 동시성과 Trade-off 하는 관계라는 점에서 실질적인 한계가 있다. 즉, 데이터무결성과 처리성능 사이에서 어느 정도 선택이 필요하다. 격리성을 완벽히 보장하기 위해 모든 트랜잭션을 순차적으로 실행한다면 동시성 처리 이슈가 발생하고, 동시성을 높이기 위해 여러 트랜잭션을 병렬처리하게 되면 데이터의 무결성이 깨질 수 있다는 것이다.

여기서 등장하는 개념이 트랜잭션의 격리 수준이다.

트랜잭션의 격리수준(Isolation Level)

동시에 여러 트랜잭션이 처리될 때, 어느 정도 수준의 격리성을 지킬 것인지에 대한 수준을 의미한다. 4가지 레벨이 있으며, 레벨 0과 3은 실무에서 좀처럼 잘 쓰이지 않는다.

  • Lv.0 : Read Uncommitted
  • Lv.1 : Read Committed
  • Lv.2 : Repeatable Read
  • Lv.3 : Serializable

Read Uncommitted

  • 다른 트랜잭션에서 일어난 변경사항이 커밋(Commit)되지 않아도, 현재 트랜잭션에서 그 변경된 값을 읽을 수 있는 격리수준이다.
  • 트랜잭션 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있게 되는 현상을 Dirty Read라고 한다.

Read Committed

  • 다른 트랜잭션에서 일어난 변경사항이 커밋되지 않았다면, 실제 테이블의 값을 가져오는 게 아니라 Undo 영역에 백업된 레코드에서 값을 가져오게 되는 격리수준이다.
  • 즉, 커밋된 결과만 읽어오므로 Dirty Read가 발생하지 않는다.
  • 하지만 현재 트랜잭션이 아직 진행중인데 다른 트랜잭션이 커밋을 해버렸다면, 그 변경결과가 트랜잭션 중에 반영되게 된다.
  • 즉, 한 트랜잭션 내에서 동일 쿼리문으로 동일 레코드를 두번 이상 조회했을 때, 상이한 결과를 얻을 수도 있다는 것이다. 이러한 현상을 Non-repeatable Read라고 한다.
  • 관계형 데이터베이스에서 대부분 기본적으로 사용되고 있는 격리 수준이다.

Repeatable Read

  • 트랜잭션이 시작되기 전에 변경(UPDATE, DELETE)된 내용에 대해서만 조회하도록 하는 격리수준이다. 먼저 일어난 트랜잭션인지 여부는 트랜잭션ID를 보고 알 수 있다.
  • 하지만 현재 트랜잭션 중간에 다른 트랜잭션에서 INSERT가 발생한 레코드는 조회가 될 수 있으며 이를 Phantom Read라고 한다.

Serializable

  • 가장 단순하면서도 가장 엄격한 격리수준이다.
  • Repeatable Read와 다른 점은, Repeatable Read의 경우 이미 있던 레코드에 대해서만 보장하는 반면, Serializable의 경우 새로 생길 레코드(INSERT되는 레코드)도 고려하여 격리성을 유지하는 것이다.
  • 읽기 작업에도 공유 Lock을 설정함에 따라, 그동안 다른 트랜잭션에서 이 레코드를 변경하지 못하게 된다.
  • 이러한 특성 때문에 동시처리 능력이 다른 격리수준보다 떨어지고, 성능저하가 발생하게 된다.
  • 모든 것이 순차적으로 진행되므로 Phantom Read가 발생하지 않는다.

[참고자료]
https://nesoy.github.io/articles/2019-05/Database-Transaction-isolation
http://egloos.zum.com/sweeper/v/3005129
https://suhwan.dev/2019/06/09/transaction-isolation-level-and-lock/