ACID 중 (Guaranteed) Consistency 즉, Data Integrity 데이터 무결성을 위해 트랜잭션을 도입했는데
트랜잭션의 궁극적인 목표는 모든 Operation 의 독립성을 보장하는 것이나, 독립성의 과도한 보장은 성능의 저하 유발
- 왜 의도적으로 Transaction 간 격리 정도를 느슨하게하나?
- 동시성 단일 시간 내 많은 “읽기” Transaction 처리 위해
(각 격리 레벨 모두 Read 를 이름에 보유)
- 각 레벨(L0 ~ L3) 별 DB 표준 에러(SQL Standard Error)와 그걸 방지하기위한 다음 레벨의 방책들
- 격리 레벨은 Transaction 간 격리 정도에 따라
느슨한 것에서부터 → 타이트한 직렬화 수준까지 정의
1. L0 : Read Uncommitted ← Dirty Read
- 커밋되지 않은 데이터 변경 사항 read 가능 : ROLLBACK, COMMIT 여부에 상관 없이 모든 값
- 가장 빠르기 때문에 데이터 일관성 혹은 정합성(Consistancy)이 조금 떨어지더라도 성능을 극대화 시 사용
- Dirty Read
: 한 트랜잭션에서 타 트랜잭션에서 커밋하지 않은 데이터 변경 사항을 read
- read 가 매우 빈번하지만 굳이 데이터 정합성이 별로 필요없는 어플리케이션에 적합

[ 사진 출처 : 트랜잭션의 격리 수준(isolation Level)이란? ]
2. L1 : Read Committed ← Non-Repeatable Read
각 데이터베이스 별 MVCC 지원 방식이 상이하나,
이 글에서는 MySQL 의 Undo 영역을 통한 MVCC 지원 예로 격리 수준을 설명할 것
L1 : Read Committed 에서부터 MVCC 가 활용
- UPDATE 로 값을 수정할 때는 Undo 영역이 아닌 실제 레코드를 변경
- Undo 영역에 백업된 데이터는 불필요해진 순간에 주기적으로 Garbage Collection
- Undo 영역에 백업된 데이터가 많이 쌓인다면 DBMS 서버 성능 저하 가능성 존재
- 이러한 변경 방식이 MVCC(Multiversion Concurrency Control)
- MySQL(InnoDB) 은 Undo 영역으로 MVCC 지원
- InnoDB 엔진은 일단 실행된 모든 쿼리를 DB (실제 레코드) 에 적용, Commit 되지 않았어도
- Consistent Read
: 현재 DB (실제 레코드) 가 아닌 Undo 영역의 Snapshot 을 읽는 것
- PostgresQL 은 Snapshot(First-Committer Win)으로 MVCC 지원
- MySQL 과 거의 동일하게 동작하나 DELETE, INSERT 에 미적용 등 다른 점들이 존재
- 다른 트랜잭션에 commit 된 변경 사항만 read 가능
: 실제 테이블 값이 아닌 Undo 영역에 백업된 값을 read
- 매 read 마다 + 실제 테이블 값이 아닌 Undo 영역에 백업된 값 read
= Consistent Read
- 다른 표현으로는 Consistent Non-locking Read
- 가장 많이 사용되는 격리 수준으로, commit 되지 않은 정보는 read 불가
- Non-Repeatable Read
: 한 트랜잭션 내 반복해서 read 할 때, 타 트랜잭션의 시작과 끝의 다른 값을 read
- 이 때문에 입출금이 빈번한 금전적 처리와 같이,
read 가 빈번한 어플리케이션에는 부적합

[ 사진 출처 : 트랜잭션의 격리 수준(isolation Level)이란? ]
3. L2 : Repeatable Read ← Phantom Read
각 데이터베이스 별 MVCC 지원 방식이 상이하나,
이 글에서는 MySQL 의 Undo 영역을 통한 MVCC 지원 예로 격리 수준을 설명할 것
- 같은 데이터 필드는 여러 번 반복해서 읽더라도 동일한 값 : 버져닝 기반으로 Undo 영역에 백업된 값만 read
- 첫 Read 시점 기준으로 + 실제 테이블 값이 아닌 Undo 영역에 백업된 값을 read = Consistent Read
- 다른 표현으로는 Consistent Non-locking Read
- 한 트랜잭션이 특정 값을 read 하는 도중에, 타 트랜잭션이 값을 뒤늦게 수정하더라도 이전 버전의 백업된 값을 read
- 일반적으론 새로운 row 를 추가하거나 기존 row 를 삭제해도 MVCC 버져닝 덕분에 Phantom Read 미발생
- 현재 트랜잭션과 다른 타 트랜잭션 버전이 무엇을 하던 어차피 버전 기반으로 무시하기 때문
- MySQL, PostgresQL 등 MVCC 를 지원하는 모든 DBMS 에서 사실상 Phantom Read 미발생
- 단, read 잠금(Exclusive Lock)을 거는 경우엔 (하지만 거의 이런 경우는 미존재)
- 일반 DBMS 는 Record Lock 이라 Phantom Read 발생
- Record Lock = 쉬운 이해를 위해서 Row Lock 이라고 생각
- Row 에 대한 UPDATE 방지 O
- Table 에 대한 INSERT, DELETE 방지 X
→ Phantom Read(INSERT, DELETE) 발생
- MySQL 은 Gap Lock 을 쓰기때문에 Phantom Read 미발생
- Gap Lock = 쉬운 이해를 위해서 Rows Lock 이라고 이해
- Rows 에 대한 INSERT, DELETE 방지 O
Table 에 대한 INSERT, DELETE 방지 X
- 하지만 일부의 Rows 에서는 Phantom Read 미발생한다는 의미
- 실제로는 Index Record 에 대한 Lock
- Phantom Read
: UPDATE(Row) 가 아닌 INSERT 혹은 DELETE 에 대해선 Lock 이 걸려있지 않아 발생
- 우리 어제 진짜 재미있었지? / 맞아! 넷이서 노니까 진짜 재밌었어 / 어? 우리 세명..이었어..

[ 사진 출처 : 트랜잭션의 격리 수준(isolation Level)이란? ]
4. L3 : Serializable
- 완전한 ACID를 보장하는 격리 수준
- 보통 해당 트랜잭션에 관련된 모든 테이블을 잠그기 때문에 성능이 가장 비효율적인 격리 수준
- SELECT 만 해도 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock
- Shared Lock : read 는 허용하나 수정, 삽입, 삭제 모두 허용하지 않겠다는 것
- 참고 : SELECT 가 아닌 트랜잭션이 수정, 삽입, 삭제 시 Exclusive Lock 적용


5. Spring Data JPA 에서 @Transactional 옵션 중 Isolation Level
Spring (Boot) 개발 시 Spring Data JPA 으로 개발할때 클래스나 메서드마다 @Transactional 세부 설정 가능