[Spring Boot] Transaction Isolation Level, @DynamicUpdate

Transaction Isolation Level
- spring이 제공하는 트랜젝션에 대한 기능으로, 동시에 발생하는 트랜잭션 간에 데이터 접근을 어떻게 처리할지에 대한 단계를 정의한다.
- 격리 단계가 올라갈수록 데이터의 정확성을 보장해주지만, 동시 접근에 대한 성능은 떨어진다.
DEFAULT
- 사용하는 DB의 디폴트 격리 단계를 따른다. mysql의 경우 REPEATABLE_READ이다.
READ_UNCOMMITTED(Level 0)
- 가장 낮은 단계로 트랜젝션에 처리중이거나, 아직 commit 되지 않은 데이터를 다른 트랜젝션이 읽는 것을 허용한다. 데이터베이스의 일관성을 유지할 수 없기 때문에 Dirty Read가 발생한다.
Dirty Read
- UPDATE, INSERT 반영 전에 데이터를 읽어 생기는 오류이다.
- 트랜잭션 1이 시작한다.
- 트랜잭션 2가 시작한다.
- 1이 데이터를 수정 후 UPDATE 혹은 INSERT를 진행한다.
- 트랜잭션 2가 UPDATE 혹은 INSERT 된 데이터를 읽는다.
- 트랜잭션 1에 문제가 생겨 롤백을 진행한다.
- 그러나 트랜잭션 2는 여전히 UPDATE 혹은 INSERT 가 된 상태의 데이터를 인식하고 있다.
READ_COMMITTED(Level 1)
- Dirty Read 현상을 방지할 수 있다.
- 트랜젝션이 수행되는 동안 다른 트랜젝션은 접근할 수 없고 대기 상태가 된다.
- commit 된 트랜잭션에 대해서만 조회가 가능하다.
- Non-Repeatable Read가 발생한다.
Non-Repeatable Read
- 하나의 트랜잭션이 같은 값을 조회할 때 다른 값이 조회되는 현상이다. 커밋된 데이터만 읽어오기 때문에 발생하는 현상이다.
- 트랜잭션 1이 시작한다.
- 트랜잭션 1이 Id=1인 데이터의 값을 malgum에서 malgummm으로 변경한다.
- 트랜잭션 2가 시작한다.
- 트랜잭션 2가 Id=1인 데이터를 조회하면 malgum으로 조회된다.
- 트랜잭션 1이 커밋하고 종료한다.
- 트랜잭션 2가 Id=1인 데이터를 다시 조회하면 malgummm으로 조회된다.
REPEATABLE_READ(Level 2)
- 트랜잭션 내에서 한번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회횐다. 즉 조회한 데이터 내용의 동일함을 보장한다.
- 다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정이 불가능하다.
- 처음 트랜잭션이 시작되면 처음의 데이터에 대해 스냅샷을 남기고, 이후에 값을 변경 후 조회해도 처음에 남긴 스냅샷에서 데이터를 가져와 데이터의 동일함을 보장해준다.
- 레벨 2의 격리 단계에서는 UPDATE한 데이터에 관해서는 정합성을 보장하지만, INSERT/DELETE에 대해서는 보장하지 않는다. 때문에 Phantom Read 문제가 발생한다.
Phantom Read
- 이름처럼 있던 데이터가 사라지거나 없던 데이터가 생기는 현상을 뜻한다.
- 트랜잭션 1이 시작한다.
- 트랙잰셕 1이 Id=1(value = malgum)인 값을 조회한다.
- 트랜잭션 2가 시작한다.
- 트랜잭션 2가 Id=1인 데이터를 malgummm으로 변경한다.(UPDATE)
- 트랜잭션 1이 Id=1인 데이터를 조회하지만 데이터는 여전히 malgum 으로 나온다.
- 트랜잭션 2가 Id=2(value = malgum2)인 데이터를 삽입한다.(INSERT)
- 트랜잭션 2가 커밋 후 종료한다.
- 트랜잭션 1이 Id=2인 데이터를 조회하면 malgum2를 확인할 수 있다.
SERIALIZABLE(Level 3)
- 가장 강력한 격리 수준으로 데이터의 일관성을 완벽하게 보장한다. 그만큼 동시 처리 성능이 가장 떨어진다. 위에서 발생한 현상들은 하나도 발생하지 않는다.
- 트랜잭션이 특정 테이블을 읽으면 다른 트랜잭션은 그 테이블의 데이터를 변경, 수정할 수 없다.
@DynamicUpdate
- 트랜젝션 테스트를 하다보면 update쿼리가 발생할 때 마다 수정한 컬럼뿐만 아니라 모든 컬럼에 대해 수정을 진행하는 것을 확인할 수 있다. 이를 @DynamicUpdate을 사용하면 방지할 수 있다.
- JPA에서 제공해주는 어노테이션으로 수정한 컬럼에 대해서만 update쿼리를 실행한다.