트랜잭션은 DB에서 동시성을 위해 사용되는 기술이다.
트랜잭션의 특징은 원자성, 일관성, 격리성, 지속성이 있다.
Atomicity (원자성): 트랜잭션이 모두 성공하거나 모두 실패하는 것을 의미한다.
Consistency (일관성): 트랜잭션이 실행 전후에 데이터베이스의 일관성을 유지해야 함을 의미한다
Isolation (격리성): 여러 트랜잭션이 동시에 실행되더라도 서로 영향을 주지 않도록 보장하는 것이다
Durability (지속성): 트랜잭션이 성공적으로 완료되면 그 결과가 영구적으로 보존되어야 함을 의미한다
이 중 트랜잭션 설정과 관련하여 격리성에 대해 더 알아보면 격리성은 다음과 같이 단계가 나누어진다.
READ UNCOMMITTED: 다른 트랜잭션이 수정 중 커밋하지 않은 데이터도 읽을 수 있음
READ COMMITED: 다른 트랜잭션이 커밋한 데이터만 반영하여 읽을 수 있음
READ REPEATABLE: 다른 트랜잭션이 커밋한 데이터만 반영하여 읽을 수 있으며, 한 트랜잭션 내에서 같은 쿼리를 수행해도 결과가 일관성 있음이 보장
SERIALIZABLE: 트랜잭션이 다른 트랜잭션의 변경 내용을 보지 못하도록 보장하며, 동시성은 제한되지만 데이터 일관성은 최고 수준으로 유지됩니다. 격리성 수준은 트랜잭션의 성격과 요구사항에 맞게 선택되어야 합니다.
PostgreSQL 에서는 다음과 같이 정도를 설정하였다. 기본은 Read committed 이다.
Dirty read 다른 트랜잭션이 수정 중이며 커밋하지 않은 데이터를 읽는 것 (특정, 범위 값을 읽었는데 다른 트랜잭션들이 커밋하든 하지 않든 수정을 하면 다시 읽을 때 데이터가 바뀜)
Nonrepeatable Read: 다른 트랜잭션이 커밋한 데이터를 반영하여 읽을 수 있으며, 같은 쿼리를 수행해도 결과가 달라질 수 있는 것 (특정 값을 읽었는데 다른 트랜잭션이 해당 값을 변경하고 커밋하면 다시 그 값을 읽을 때 바뀌어있음)
Phantom Read: 다른 트랜잭션이 커밋한 데이터를 반영하여 읽을 수 있으며, 같은 쿼리를 수행해도 결과 집합이 달라질 수 있는 것 (범위로 데이터를 조회할 때 중간에 insert가 발생하면 이후 다시 read 시, 새로운 데이터가 들어와있음)
Serialization Anomaly: 트랜잭션 격리 수준 중에서 발생할 수 있는 문제로, 동시에 실행되는 여러 트랜잭션들이 순서에 따라 실행되는 것과 다른 결과를 얻는 현상
그럼 @Transactional, @Transactional(readOnly = true) 얘들은 뭐하는 애들일까
@Transactional: 다양한 설정이 가능하지만 모든 값을 default로 설정함, isolation level 역시 DB의 default 값을 따름
@Transactional(readOnly = true): isolation level은 역시 DB의 default 값이고, 트랜잭션 내에서 데이터 수정 쿼리가 없을 때 사용, 해당 row에 대한 스냅샷을 생성하지 않아, 성능 최적화 및 가독성 향상의 용도로 사용
@Transactional(isolation = ?): 옵션을 통해 isolation level 을 지정해줄 수 있다.
그러므로, 동시성을 보장할 필요가 있는 우리의 Spring boot 프로젝트에서는 수정이 없을 경우에 @Transactional(readOnly=true) 를, 데이터 수정이 발생할 경우 @Transactional 을 사용하자.
추가.
단일 조회 쿼리는 특별한 상황(read lock, 일관성이 정말 중요함)이 아니라면 동시에 이용하는 서비스에도 트랜잭션이 필요하지 않음