트랜잭션 ACID
- A(Atomicity) 원자성
- 트랜잭션 내에서 실행한 작업들은 하나의 작업인 것 처럼 모두 성공하거나 모두 실패해야 함
- C(Consistency) 일관성
- 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야함
- 데이터베이스 무결성 제약 조건을 만족해야함
- I(Isolation) 격리성
- 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야 함
- 트랜잭션 격리 수준을 선택할 수 있음
- 격리 수준이 낮을 수록 동시성은 증가하지만 문제가 발생할 수 있음
- 격리 수준이 높으면 트랜잭션을 거의 차례대로 실행해야 함
- D(Durability) 지속성
- 트랜잭션을 성공적으로 끝내면 항상 결과가 기록되어야 함
트랜잭션 격리 수준에 따른 문제점
- DIRTY READ
- NON-REPEATABLE READ
- PHANTOM READ
트랜잭션 격리 수준
- READ UNCOMMITED(커밋되지 않은 읽기)
- 트랜잭션1에서 수정중인 데이터를 트랜잭션2에서 조회할 수 있음. 이것을 DIRTY READ라고 함
- 트랜잭션2에서 트랜잭션1에서 수정한 데이터를 사용하는데 트랜잭션1이 롤백하면 데이터 정합성에 문제가 발생할 수 있음
- READ COMMITED(커밋된 읽기)
- 커밋된 데이터만 읽을 수 있음. 따라서 DIRTY READ는 발생하지 않음
- NON-REPEATABLE READ는 발생할 수 있음
- 트랜잭션1이 A 데이터를 조회중인데, 트랜잭션2가 A 데이터를 수정하고 커밋하면 트랜잭션1이 A데이터를 다시 조회했을 때 수정된 데이터가 조회됨
- 이처럼 반복해서 같은 데이터를 읽을 수 없는 상태를 NON-REPEATABLE READ라 함
- REPEATABLE READ(반복 가능한 읽기)
- 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록 함. 즉 NON-REPEATABLE READ를 허용하지 않음
- PHANTOM READ는 발생할 수 있음
- 트랜잭션1이 10살 이하의 회원을 조회했는데 트랜잭션2가 5살 회원을 추가하고 커밋하면 트랜잭션1이 다시 10살 이하의 회원을 조회했을 때 회원 하나가 추가된 상태로 조회됨
- 반복 조회시 결과 집합이 달라지는 것을 PHANTOM READ라 함
- SERIALIZABLE(직렬화 가능)
- 가장 엄격한 트랜잭션 격리 수준으로 PHANTOM READ가 발생하지 않음
자동커밋과 수동커밋
- 자동 커밋은 각각의 SQL 실행할 때마다 자동으로 커밋
- 수동 커밋 모드로 설정하는 것을 트랜잭션을 시작한다고 표현함
- 트랜잭션을 시작하고 커밋하거나 롤백할 수 있음
락
- 한 트랜잭션이 데이터를 수정하는 동안 커밋이나 롤백 전까지는 다른 트랜잭션이 해당 데이터를 수정할 수 없게 막아야 함
- 수정하려는 로우의 락을 먼저 획득해야 해당 로우의 데이터를 수정할 수 있음
- 다른 트랜잭션이 데이터를 변경하려고 할 경우 락이 돌아올 때 까지 대기함(타임아웃 있음)
- 커밋 혹은 롤백을 수행하여 트랜잭션이 종료되면 락이 반납됨
- 락을 기다리고 있던 트랜잭션이 락을 획득하여 데이터를 수정함
- 일반적으로 데이터를 조회할 때에는 락을 획득하지 않고 바로 조회할 수 있음
- 데이터를 조회할 때에도 락을 획득할 수 있음. 트랜잭션 종료 시점까지 해당 데이터를 다른 곳에서 변경하지 못하도록 막을 때 사용함
비관적 락
- 데이터베이스에서 제공하는 트랜잭션 락 매커니즘
- 데이터베이스에 쓰기 락을 걸어 다른 트랜잭션이 수정할 수 없음
- NON-REPEATABLE READ 방지
낙관적 락
- JPA 에서 제공하는 버전 관리 기능을 사용한 매커니즘 (애플리케이션 레벨에서 제공하는 기능)
- 엔티티에 버전 관리용 필드를 추가하여 수정할 때마다 버전이 자동으로 증가함
- 엔티티 수정시 조회 시점 버전과 수정 시점 버전이 다르면 예외가 발생함
- 예를 들어 트랜잭션 1이 조회한 엔티티를 수정하고 있는데 트랜잭션 2 에서 같은 엔티티를 수정하고 커밋하여 버전이 증가하면 트랜잭션 1이 커밋할 때 버전이 달라 예외가 발생함