시작 전에 읽어두기
- 트랜잭션
- DB 상태를 변화시키기 위해 수행하는 작업 단위
- 즉, 유저(서비스)가 정의한 쿼리 묶음
- Ex. 로그인 : 중복 ID 확인 (select)와 신규 계정 생성 (insert)
- 트랜잭션의 ACID
- 원자성 (Atomicity) : 트랜잭션은 완전히 성공하거나 완전히 실패하거나 둘 중 하나의 상태를 가져야 한다.
- 일관성 (Consistency) : 트랜잭션의 작업 처리 결과는 항상 일관적이어야 한다.
- Ex. 트랜잭션 진행 중 DB가 변경되어도, 처음 참조한 DB로 트랜잭션을 진행해야 한다.
- 격리성 (Isolation) : 둘 이상의 트랜잭션이 실행될 때, 그 어떤 트랜잭션도 다른 트랜잭션의 연산을 침범할 수 없다.
- 지속성 (Durability) : 트랜잭션이 성공적으로 완료되었을 때, 해당 트랜잭션의 결과는 DB에 영구적으로 반영되어야 한다.
전파 속성 (Propagation)
spring boot @Transactional에서는 Propagation.REQUIRED가 default로 세팅되어 있다
전파 속성이란?
Spring Boot @Transactional 옵션 정리
- 부모 트랜잭션 : 호출한 메소드의 트랜잭션
- 자식 트랜잭션 : 호출당한 메소드의 트랜잭션
- Propagation.REQUIRED
- 부모 트랜잭션이 존재한다면, 자식 트랜잭션은 부모에게 포함된다.
- 즉, 자식 트랜잭션이 실패하면 부모도 롤백된다
- 부모 트랜잭션이 존재하지 않다면, 새로운 트랜잭션을 생성한다 (즉 자식이 부모가 된다)
- Propagation.REQUIRES_NEW
- 부모 트랜잭션의 존재 유무와 관계없이, 새로운 트랜잭션을 생성한다.
- Propagation.NESTED
- 부모 트랜잭션이 존재할 경우, 부모 트랜잭션 안에 새로운 트랜잭션을 생성한다.
- 단, 자식 트랜잭션은 부모 트랜잭션의 영향(커밋, 롤백)을 받지만 부모는 자식의 영향을 받지 않는다.
- 부모를 롤백하면 자식도 롤백되지만, 자식을 롤백해도 부모는 영향 X
- Propagation.MANDATORY
- 부모가 존재하면 부모에 포함
- 부모가 존재하지 않는다면 예외 발생
- Propagation.SUPPORTS
- 부모가 존재하면 부모에 포함
- 부모가 존재하지 않는다면 트랜잭션 없이 동작
- Propagation.NOT_SUPPORTED
- 부모가 존재하면 부모 트랜잭션을 보류하고, 트랜잭션 없이 동작
- 부모가 존재하지 않는다면 트랜잭션 없이 동작
- Propagation.NEVER
- 트랜잭션을 사용하지 않도록 강제한다.
- 부모가 존재할 경우, 예외 발생
고립 레벨 (Isolation)
spring boot @Transactional에서는 Isolation.DEFAULT, 즉 JDBC isolation level가 default로 세팅되어 있다.
:: 즉 MySQL InnoDB를 사용한다면 REPEATABLE READ 사용
고립 레벨이란?
- Shared Lock (공유 잠금) : 읽기 잠금
- Exclusive Lock (배타적 잠금) : Insert, Update, Delete (=쓰기) 잠금
- SQL의 Isolation level과 동일하게 동작
- SQL의 고립 레벨 (= 고립 수준 = 격리 레벨 = 격리수준)이란?
- 트랜잭션끼리 일관성 없는 데이터를 허용하는 수준
- 종류
- [Level 0] Read Uncommitted : select 수행하는 동안 해당 데이터에 Shared lock이 걸리지 않는다.
- [Level 1] Read Committed : select 수행하는 동안 해당 데이터에 Shared lock이 걸린다.
- [Level 2] Repeatable Read : 트랜잭션이 완료될 때까지 해당 데이터 수정이 불가능하다.
- [Level 3] Serializable : 트랜잭션이 완료될 때까지 해당 데이터 수정, 입력이 불가능하다.
고립 레벨별 나타나는 이슈
- Dirty Read : Uncommitted 또는 더티 버퍼에 있는 데이터를 읽어, 롤백된 데이터를 노출하게 됨
- Non Repeatable Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행 할 때, A와 B 사이에 수정 또는 삭제가 일어나 A와 B의 결과가 달라짐 (=행 값의 변화)
- Phantom Read : 동일한 쿼리를 2번(A와 B라고 가정) 실행할 때, A와 B 사이에 삽입이 일어나 A와 B의 결과가 달라짐 (=행 수의 변화)
Isolation Level | Dirty Read | Non Repeatable Read | Phantom Read |
---|
READ UNCOMMITTED | Permitted | Permitted | Permitted |
READ COMMITTED | | Permitted | Permitted |
REPEATABLE READ | | | Permitted |
SERIALIZABLE | | | |
DB별 default isolation
- MySQL InnoDB : REPEATABLE READ
- Oracle : READ COMMITTED
Spring Boot @Transactional 옵션 정리
SQL의 고립레벨과 동일!!!
Ex. 홍길동씨와 강동원씨가 공유 통장에 대해 아래 작업을 요청할 때
1. 통장 잔고 확인 (=select)
2. 통장 잔고 수정 (=update)
- Isolation.READ_UNCOMMITTED (level 0)
- 홍길동씨가 잔고 수정 작업을 끝마치기 전, 강동원씨는 통장 잔고를 확인할 수 있다.
- Isolation.READ_COMMITTED (level 1)
- 홍길동씨가 잔고 수정을 끝마치기 전까지, 강동원씨는 통장 잔고를 확인할 수 없다.
- Isolation.REPEATABLE_READ (level 2)
- 홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다.
- 단, 입금의 경우 (=insert) 처리할 수 있다. 하지만 홍길동씨와 강동원씨의 잔고 확인 결과가 달라질 수 있다.
- Isolation.SERIALIZABLE (level 3)
- 홍길동씨가 잔고 확인, 잔고 수정 작업을 끝마칠 때까지 강동원씨는 아무 작업도 시작할 수 없다 (입금도 불가능).