데이터베이스 트랜잭션과 고립화 레벨

트랜잭션 (Transaction)

what: 데이터베이스의 상태를 변환시키는 작업 단위를 이야기 한다.

트랜잭션이 왜 필요한가

관련있는 작업의 단위를 묶어서, 한번에 처리하거나 문제가 발생한경우 롤백을 용이하게 하기 위해서 사용한다.

  • 만약 두개의 테이블이 있다고 가정하자.
    • 주문번호, 날짜, 고객번호를 가진 테이블 A
    • 주문번호, 상품코드, 주문한 상품개수를 가진 테이블 B
  • 주문이 발생했다고 생각하자.
    • 주문번호가 생성되고, 현재 시간과, 주문한 고객정보가 테이블 A 에 추가가 될것이다.
    • 이때 테이블 B 의 정보도 같이 추가되어야 한다.
    • 만약 고객이 여러종류, 여러개의 상품을 구매했다면 테이블 B 에는 여러 행이 추가 될것이다.
  • 에러가 발생했다.
    • 테이블 B 의 4번째 행을 추가할때 에러가 발생했다고 하자.
    • 주문 자체가 취소되어야 하여, 1,2,3 번째의 행을 삭제해주어야 한다.
    • 이것을 일일히 다 삭제하고 하려면 굉장히 번거롭고 귀찮은 작업이다.

이런 작업들을 한번에 묶어서 처리할수 없을까??

  • 트랜잭션 을 이용한다.

트랜잭션의 임시공간

트랜잭션은 여러개의 SQL 명령을 한개의 작업으로 묶을 수 있다.

  • 이때 SQL 명령의 결과들은 임시공간에 저장되고 있고, COMMIT을 하게 되면 실제 테이블에 적용이 된다.
  • COMMIT : 트랜잭션을 통해 변경된 사항을 적용하는 명령
  • ROLLBACK : 변경된 사항을 적용하지 않고 종료하는 명령
    • 위의 주문에러 예시의 경우, ROLLBACK 을 하면 깔끔하게 처리가 된다.

트랜잭션 고립화 레벨 (Isolation Level)

what: 트랜잭션 실행중 다른 연산이 접근 불가능하게 하여, 고립하는 수준

트랜잭션 고립화 레벨을 왜 알아야하는가 (WHY)

트랜잭션에서 일관성이 없는 데이터를 허용도록 하는 수준을 알고 있어야, 데이터 베이스 관리가 용이해진다.

  • 데이터베이스는 무결성을 중요하는것이 무엇보다 중요하다.
  • 트랜잭션은 여러개의 SQL 명령을 처리 하는데, 이때 중간에 다른 트랜잭션이나 SQL 명령으로 인해 데이터 베이스내 데이터에 영항을 준다면 일관성과 무결성이 무너질 수 있다.
  • 이것을 방지하기 위해 한 트랜잭션이 처리중일때, 다른 트랜잭션은 DB에 접근하지 못하게 하는 Locking 이라는 방법을 쓴다.
    • 하지만 과도한 Locking 은 성능을 떨어뜨리게 된다.
  • 이런 Locking 을 효율적으로 사용하는 방법을 분류한것이 Iolation Level이다.

Isolation Level의 종류

Read Uncommited

트랜잭션에서의 변경내용이 COMMIT되기전, 다른 트랜잭션에서 값을 읽을 수 읽을 수 있다.

  • 트랜잭션 처리된 작업의 중간 결과를 볼 수 있다.

발생할 수 있는 문제점 (DirtyRead)

  • 한 트랜잭션이 A를 B로 변경하고 있을때
  • 다른 트랜잭션이 A의 위치를 읽게 되면
  • A가 아닌 B라는 결과를 얻을 수 있다. (DirtyRead)
  • 이때 Uncommited 라는 단어처럼 아직 완료되지 않은 작업에 대한 접근이 가능해지는것이다.

Read Commited

커밋된 데이터만 읽는다.

  • 한 트랜잭션이 A를 B로 변경하고 있을때
  • 다른 트랙잭션은 변경되고 있는 A의 위치를 읽을 수 없다,
  • 대신 스냅샷을 이용해 백업되어있는 값A 을 가져오게 된다.
  • 하지만 A를 B로 변경하는데 성공하고 Commit을 했다면, 이제는 다른 스냅샷에 B가 있으므로 B값을 가져오게된다. (Non-Repetable Read)

발생할 수 있는 문제점: (Non-Repetable Read)

  • 한 트랜잭션에서 같은 쿼리를 두번 실행했을때, 수정된 데이터를 다르게 읽기 때문에 다른결과가 나올 수 있다.

Repeatable Read

하나의 트랜잭션에서는 본인이 수정하지 않은 데이터에 대해서는 같은 결과를 보장한다.

  • 트랜잭션이 완료될때까지 사용하는 데이터에는 Locking이 걸리게 된다.
  • 하지만 범위 자체에 대한 Locking은 걸리지 않는다.
  • 또한 트랜잭션이 직접적으로 변경하거나 접근하고 있는 데이터를 제외한 다른 데이터에는 접근이 가능하다. (Update)
  • 삽입도 가능하다 (Insert)
  • MYSQL 에서 기본적으로 제공하는 격리 레벨이다.

  • col라는 열의 데이터에는 {A B C D} 가 있고
  • 트랜잭션1이 col의 데이터중 AC에 수정하고 있다면
  • 트랜잭션2는 나머지인 BD에 접근할 수 있다.

  • 트랜잭션에서 A라는 데이터에 접근했다면, 실제로는 A라는 데이터가 변경되었어도 한 트랜잭션 내에서는 계속해서 A라는 일관된 값을 얻어올 수 있다.
  • 이것이 가능한 원리는, MYSQL 기준 트랜잭션 id는 실행순서에 따라 오름차순으로 저장되어있는데 항상 자기보다 낮은 트랜잭션id보다 작은것에서 변경한것만 읽기 때문이다.
  • 만약 실제 데이터가 자기보다 높은 트랜잭션id가 적혀있다면, Undo영역의 데이터를 가져오게 된다.
    • 트랜잭션1: A읽음 (1001번 트랜잭션 begin())
    • 트랜잭션2: A를 B로 변경시작 (1002번 트랜잭션 begin())
    • 트랜잭션2: A를 B로 변경완료 (1002번 트랜잭션 end())
    • 트랜잭션1: A읽음 (B로 변경한 1002번 트랙잭션 결과 무시 1002 > 1001, Undo 영역의 A를 읽음)

발생할 수 있는 문제점: (Phantom Read)

  • 삽입하지 않은놈이 갑자기 보이거나, 삭제하지도 않았는데 있던놈이 사라지는 경우가 발생 할 수 있다.
  • 이 문제는 자신이 타켓 자료를 변경하지 않는 이상, 처음 조회한 그 형상 그대로를 유지한다는 특징때문에 발생한다.
  • 만약 다른 트랜잭션에서 그 자료를 조작하고 커밋을 했다고 하더라도 말이다.
id value
1 A
2 B
3 C
4 D
  • col라는 열의 데이터에는 {A B C D}가 있다고 가정해보자
  • 1번 트랜잭션이 먼저 데이터 E를 넣고, 아직 커밋을 하지 않았다고 해보자.
  • 이후 시작된 2번 트랜잭션에서 전체 데이터을 읽으면, {A B C D}가 보이게된다.
  • 그리고 2번 트랜잭션이 F라는 데이터를 삽입한다고 해보자.
  • 1번 트랜잭션이 변경을 완료한뒤 커밋을 한다.
  • 2번 트랜잭션이 F삽입 작업을 마친뒤 전체 데이터를 읽으면 아래와 같은 결과를 얻게된다.
id value
1 A
2 B
3 C
4 D
5 E
6 F
  • 범위에 대한 락이 걸리지 않았기때문에, 이전에 없었던 E가 보인다.
  • 한 트랜잭션 내에서 삽입하지 않은놈이 갑자기 보이는 현상 이다. (Phantom Read)

삭제 예시

id value
1 A
2 B
  • 2번 트랜잭션이 끝나기전, 2번 보다 늦게 시작한 3번 트랜잭션에서 1번 id를 가진 데이터 A삭제하고 커밋까지 완료했다고 해보자.


id value
2 B

커밋까지 완료된 실제 데이터(3번 트랜잭션에서의 작업내용)

id value
1 A
2 B

2번 트랜젝션에서 보이는 잘못된 데이터


  • 2번 트랜잭션에서 1번 id에 대한 데이터는 보이지만, 업데이트를 하는 경우 실패하는 현상이 일어날 수 있다. (눈에는 보이지만, 실제로는 없어진 현상)
  • 왜냐하면 이전의 스냅샷만 계속 보기 때문에 처음 조회한 그 형상 그대로를 계속 보게 되기 때문이다.

Serializable

  • Repeatable Read 에서의 부분 접근조차 막아버린다.
  • 직렬성 위반이 발생하지 않는다.
  • ACID중 Consistency(일관성) 를 완전히 지킬 수 있게된다.
  • 하지만, 성능이 사용하기 힘들정도록 낮아지게 되기때문에 잘 사용하지 않는 레벨이다.

Conclustion

  • 트랜잭션을 이용해, 데이터 베이스 상태를 변경시키는 여러개의 쿼리를 한개의 작업으로 묶을 수 있다.
  • 트랜잭션 격리 레벨을 통해, 데이터베이스내의 데이터 일관성을 보장하는 정도를 정할 수 있다.
  • 격리 레벨의 정도에 따라서, 발생할 수 있는 데이터 일관성 위반의 경우의 수가 다르다.
Isolation Level \ Non Consistency Dirty Read Non-Repeatable Read Phantom Read
Read Uncommited o o o
Read commited x o o
Repeatable Read x x o
Serializable x x x

Reference

  • https://en.wikipedia.org/wiki/Isolation_(database_systems)#Phantom_reads
  • https://goodgid.github.io/Transaction-Isolation-Level/
  • https://mysqldba.tistory.com/334
  • https://nesoy.github.io/articles/2019-05/Database-Transaction-isolation
  • https://www.postgresql.kr/blog/pg_phantom_read.html
  • https://www.youtube.com/watch?v=e9PC0sroCzc&t=416s&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9CTech
  • https://robin00q.tistory.com/19
  • https://needjarvis.tistory.com/161