Spring에서는 @Transactional을 통해 트랜잭션을 선언적으로 처리할 수 있고, 그 안의 isolation 옵션은 트랜잭션 간 동시성 제어의 수준을 지정해주는 설정입니다.
그중에서도 Isolation.SERIALIZABLE은 가장 높은 수준의 격리 수준으로, 모든 트랜잭션이 마치 하나씩 순서대로 실행되는 것처럼 보장해줍니다.
여러 트랜잭션이 동시에 실행될 때, 서로 간의 읽기/쓰기 충돌을 얼마나 허용할지를 결정하는 규칙입니다.
트랜잭션 간 발생할 수 있는 3가지 주요 문제
| 문제 이름 | 설명 | 막는 격리 수준 |
|---|---|---|
| Dirty Read | 아직 커밋되지 않은 값을 다른 트랜잭션이 읽음 | READ COMMITTED 이상 |
| Non-Repeatable Read | 같은 쿼리를 두 번 했는데 결과가 달라짐 | REPEATABLE READ 이상 |
| Phantom Read | 조건은 같은데 행 개수가 달라짐 (새 row 등장) | SERIALIZABLE만 방지 |
@Transactional(isolation = Isolation.SERIALIZABLE)
public void doSomething() {
// 이 안의 모든 DB 접근은 완전히 직렬화된 것처럼 동작함
}
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void insertUserIfNotExists(String name) {
if (userRepository.findByName(name).isEmpty()) {
// 동시에 들어오면 두 트랜잭션 모두 여기 통과함
userRepository.save(new User(name));
}
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void insertUserIfNotExists(String name) {
if (userRepository.findByName(name).isEmpty()) {
userRepository.save(new User(name));
}
}