> For the complete documentation index, see [llms.txt](https://jmxx219.gitbook.io/cs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jmxx219.gitbook.io/cs/database/undefined-5.md).

# 트랜잭션의 격리성(Transaction Isolation)

## 배경

[**트랜잭션의 성질(ACID)**](https://jmxx219.gitbook.io/cs/database/pages/yCCWEP7Q8RvonMIVFW9f#트랜잭션의-성질acid)

* 원자성 (Atomicity)
* 일관성 (Consistency)
* **`격리성 (Isolation)`**
* 영속성 (Durability)

<br>

**격리성**

* 실행중인 트랜잭션의 중간결과를 다른 트랜잭션이 `접근`할 수 없음
  * 이때 `접근 레벨`이 존재함
    * 접근 레벨에 따라 다른 강도 설정
    * DB별로 설정 가능

\ <br>

## 격리성으로 인해 발생가능한 문제점

### Dirty Read

* 개념
  * 다른 트랜잭션에 의해 수정됐지만, 아직 커밋되지 않은 데이터를 읽는 것
* 상황
  * `트랜잭션A`가 정상처리 되지 않고 Rollback 될 수 있는데 `트랜잭션B`가 해당 값을 읽은 경우
    * `트랜잭션B`는 잘못된 값을 가지고 본인의 로직을 처리하는 상태에 놓임
    * 즉, `트랜잭션B`는 무결성이 깨진 데이터를 사용하게 됨
* 특징
  * 지나친 부정합 문제 발생
    * 데이터가 조회되었다 사라지는 현상을 초래해 시스템에 상당한 혼란을 줌
    * RDBMS 표준에서 인정하지 않을 정도로 정합성에 문제가 많은 격리 수준

<br>

### Non-Repeatable Read

* 개념
  * 한 트랜잭션 내에서 같은 Key를 가진 Row를 두 번 읽었는데, 그 사이에 값이 변경되거나 삭제되어 결과가 다르게 나타나는 현상
* 상황
  * `트랜잭션B`가 특정 ROW에서 2번 READ 함
  * 이 사이에 `트랜잭션A`가 해당 ROW를 Update 혹은 Delete 후 Commit 함
  * `트랜잭션B`가 다시 READ할때 값이 다르게 나옴
* 특징
  * 일반적인 경우에는 크게 문제가 되지 않음
  * 금전적인 처리와 연결될 시 문제 발생 가능

<br>

### Phantom Read

* 개념
  * 한 트랜잭션 내에서 같은 쿼리를 두 번 수행했는데, 첫 번째 쿼리에서 없던 유령(Phantom) 레코드가 두 번째 쿼리에서 나타나는 현상
* 상황
  * `트랜잭션B`가 특정 조건을 데이터를 검색해 결과를 얻음 (`트랜잭션B`는 아직 끝나지 않은 상태)
  * 이때 `트랜잭션A`가 해당 조건의 일부를 추가/삭제하고 Commit 함
    * (아직 끝나지 않은)`트랜잭션B`가 해당 조건으로 데이터 조회 시, `트랜잭션A`에서 추가/삭제한 데이터가 함께 조회/누락 됨

<br>

> COMMIT : 트랜잭션을 종료하고 다른 사용자에게 변경된 모든 사항을 보이게 함.

<br>

> **Phantom Read와 Non-Repeatable Read**\
> Non-Repeatable Read, 한 개의 Row의 데이터 값이 변경되는 것(Update 또는 Delete) Phantom Read, 다수의 건을 요청하는 것에 대해 데이터의 값이 변경되는 것

\ <br>

## 트랜잭션의 격리 수준

* 개념
  * 여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 여부를 결정하는 것
* DB에서 제공하는 격리성 수준
  * DBMS에 따라 내부적 격리성 제공
* ANSI/ISO SQL 표준(SQL92)
  * 트랜잭션의 격리성과 동시 처리 성능 사이의 Trade-Off를 두고 격리성 접근 레벨을 4단계로 나눔
* Trade-Off 관계
  * 높은 격리 수준
    * 높은 데이터 정합성: 격리성으로 인한 이슈 적게 발생
    * 낮은 동시 처리 성능
  * 낮은 격리 수준
    * 낮은 데이터 정합성
    * 높은 동시 처리 성능
* [LOCK](/cs/database/db-locking.md)
  * 트랜잭션 발생 시 DB 내부적으로 DB Lock이 걸림

\
\ <br>

## 트랜잭션 격리 단계

> 격리수준이 **낮음 → 높음** 순서로 진행

<br>

### ◻️ Read Uncommitted

* 개념
  * 트랜잭션에서 처리 중인 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용
  * 커밋하지 않은 데이터 조차도 접근할 수 있는 격리 수준
* 특징
  * 정합성의 문제로 권장하지 않음
* 발생 가능 문제점
  * `Dirty Read`
  * `Non-Repeatable Read`
  * `Phanton Read`

\ <br>

### ◻️ Read Committed

* 개념
  * 트랜잭션이 **커밋되어 확정된 데이터만** 다른 트랜잭션이 읽도록 허용
* 특징
  * 커밋 되지 않은 데이터에 대해서는 실제 DB 데이터가 아닌 Undo 로그에 있는 이전 데이터를 가져옴
  * Dirty Read의 발생가능성 막음
  * 오라클의 기본 트랜잭션 격리 수준
* 발생 가능 문제점
  * `Non-Repeatable Read`
  * `Phanton Read`

\ <br>

### ◻️ Repeatable Read

* 개념
  * 트랜잭션 범위 내에서 조회한 내용이 항상 동일함을 보장하는 트랜잭션 격리수준
  * `MVCC`를 이용해 한 트랜잭션 내에서 동일한 결과를 보장하지만, 새로운 레코드가 추가되는 경우 부정합 발생 가능
* 특징
  * [MySQL의 InnoDB 엔진](/cs/database/mysql-innodb.md)의 기본 트랜잭션 격리 수준
  * 어떤 트랜잭션이 읽은 데이터를 다른 트랜잭션이 수정하더라도 동일한 결과를 반환할 것을 보장
    * 트랜잭션 번호를 참고하여 자신보다 `먼저` 실행된 트랜잭션의 데이터만을 조회
      * 테이블에 자신 이후에 실행된 트랜잭션의 데이터가 존재할 경우 UNDO로그 참고로 데이터 조회
  * 새로운 레코드의 추가까지는 막지 않음
* 동작 방식

  ![](https://blog.kakaocdn.net/dn/pzv2A/btsgE65gQXu/pmPaiO0dFFbzKdMYd5UFgk/img.png)

  1. `사용자B`가 id=50인 레코드 조회 (`트랜잭션B`)
     * 테이블 값(조회 결과): MangKyu
  2. `사용자A`가 id=50인 레코드 갱신(`트랜잭션A`, 아직 트랜잭션B 종료되지 않음)
     * MVCC를 통해 기존 데이터는 변경, 백업된 데이터는 UNDO 로그에 남음
     * 테이블 값(조회 결과): MinKyu
     * UNDO로그: MangKyu
  3. (트랜잭션B가 끝나지 않은 상태에서)`사용자B`가 다시 동일한 SELECT문 실행
     * UNDO로그(조회 결과): MangKyu
     * 수정된 `트랜잭션A`가 아닌 이전의 `트랜잭션B`의 값이 나옴
* 발생 가능 문제점
  * `Phanton Read`
    * 일반적인 MySQL 조회에선 발생하지 않음
      * 새로운 레코드의 추가를 막지않아 발생 가능하나 MVCC 덕분에 일반적인 조회에선 발생하지 않음
    * UNDO로그가 아닌 테이블로부터 데이터를 가져올때 발생할 수 있으나 해당 케이스는 거의 존재하지 않음

\ <br>

**MVCC (다중 버전 동시성 제어)**

* 개념
  * 동시 접근을 허용하는 DB에서 동시성을 제어하기 위해 사용하는 방법 중 하나
  * 동일한 레코드에 대해 여러 버전의 데이터가 존재하는 것
    * 일반적인 RDBMS에서는 변경전 레코드를 UNDO영역에 백업해두어 변경 전, 후 데이터가 모두 존재
* 특징
  * 트랜잭션이 롤백된 경우 데이터 복원 가능
  * 서로 다른 트랜잭션간 접근할 수 있는 데이터 세밀하게 제어
  * 트랜잭션 번호
    * 각각의 트랜잭션은 순차 증가하는 고유한 트랜잭션 번호 존재
    * 백업 레코드에 어느 트랜잭션에 의해 백업되었는지 트랜잭션 번호를 함께 저장

\ <br>

**MySQL에서 Repeatable Read를 기본 격리수준으로 사용하는 이유**

* 격리 수준과 서버 처리 성능
  * SERIALIZABLE이 아니라면 격리 수준이 높다고 MySQL서버 처리 성능 개선 및 저하가 크게 발생하지 않음
  * 결국 UNDO로그를 통해 레코드를 참조하는 과정이 거의 동일하기 때문
* REPEATABLE READ
  * READ COMMITTED보다 동시 처리 성능이 뛰어남
  * 갭 락을 통해 Phantom Read까지 거의 발생하지 않음

\ <br>

### ◻️ Serializable Read

* 개념
  * 트랜잭션을 순차적으로 실행
* 특징

  * 가장 엄격한 수준의 격리 수준
  * 어떠한 데이터 부정합 문제도 발생 X
    * 여러 트랜잭션이 동일한 레코드에 동시에 접근할 수 없음
  * 매우 낮은 동시 처리 성능
    * 트랜잭션이 순차적으로 처리되야 함

  > 가장 안전하나 가장 성능이 떨어지므로, 극단적으로 안전한 작업이 필요한 경우가 아니면 사용하지 않음

  * 순수한 SELECT 작업에서도 대상 레코드에 넥스트 키 락을 읽기 잠금(Shared lock)으로 건다.

\ <br>

### Ref

[트랜잭션 격리성 수준](https://shuu.tistory.com/76)\
[\[MySQL\] 트랜잭션의 격리 수준(Isolation Level)에 대해 쉽고 완벽하게 이해하기](https://mangkyu.tistory.com/299)
