@Transactional
•
데이터베이스 트랜잭션은 데이터베이스 관리 시스템 또는 유사한 시스템에서 상호작용의 단위이다.
◦
쉽게 말하면 더 이상 쪼개질 수 없는 최소의 연산이라는 의미가 된다.
•
@Transactional을 클래스 또는 메소드 레벨에 명시하면 해당 메소드 호출시 지정된 트랜잭션이 작동한다. (선언적 트랜잭션)
◦
해당 클래스의 Bean을 다른 클래스의 Bean에서 호출할 때만 @Transactional을 인지하고 작동하게 된다.
◦
같은 Bean 내에서 @Transactional이 명시된 다른 메소드를 호출해도 작동하지 않는다.
◦
Spring Framework는 내부적으로 AOP를 통해 해당 어노테이션을 인지하여 프록시를 생성하여 트랜잭션을 자동 관리하기 때문이다.
▪
클래스, 메소드 레벨에 @Transactional이 명시되면, 이 클래스에 트랜잭션 기능이 적용된 프록시 객체가 생성된다.
▪
이 프록시 객체는 PlatformTransactionManager를 사용하여 트랜잭션을 시작하고, 정상 여부에 따라 Commit 또는 Rollback 한다.
•
@Transactional이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소한다. (롤백)
특성
데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기위한 성질 네가지
•
원자성(Atomicity)
◦
한 트랜잭션 내에서 실행한 작업들은 하나로 간주한다. 즉, 모두 성공 또는 모두 실패.
•
일관성(Consistency)
◦
트랜잭션은 일관성 있는 DB 상태를 유지한다. (data integrity 만족 등.)
•
격리성(Isolation)
◦
동시에 실행되는 트랜잭션들이 서로 영향을 미치지 않도록 격리해야한다.
•
지속성(Durability)
◦
트랜잭션을 성공적으로 마치면 결과가 항상 저장되어야 한다.
Option
Isolation (격리 수준)
•
동시에 여러 사용자가 데이터에 접근할 때 어디까지 허용할까? 를 정하는 옵션
•
트랜잭션의 격리 수준과 데이터의 일관성은 비례한다.
◦
격리 수준이 약할수록 데이터 접근 및 수정이 자유롭지만 일관성이 떨어지고, 격리수준이 강해진다면 데이터의 일관성은 증가한다.
Default
•
사용하는 데이터베이스의 기본 격리 수준을 따른다.
READ_UNCOMMITTED
•
한 트랜잭션이 처리중인 커밋되지 않은 데이터를 다른 트랜잭션에서 접근 가능하다.
•
DB에 커밋하지 않은, (DB에 존재하지 않는) 데이터를 읽는 현상을 Dirty Read라고 한다.
•
데이터 정합성에 문제가 많아 웬만하면 권장되지 않고, 아예 지원하지 않는 경우도 있다.
•
Dirty Read가 가능하기 때문에, 잘못된 데이터를 읽을 수 있다.
◦
A 트랜잭션이 데이터 1을 조회하여, 2로 변경하고 아직 커밋하지 않음
◦
B 트랜잭션이 동일한 데이터를 조회해서 2라는 값을 받음 (Dirty Read)
◦
A 트랜잭션에서 오류가 발생해서 데이터를 롤백 (2 → 1)
◦
실제 데이터는 1이지만, B트랜잭션은 2라는 잘못된 데이터를 읽는다.
READ_COMMITTED
•
트랜잭션은 커밋한 데이터만 읽을 수 있다.
•
A트랜잭션이 데이터를 변경해도 커밋하기 전이라면 B트랜잭션은 변경되기 전의 데이터를 조회할 수 있다.
◦
이 때, B트랜잭션은 Undo 영역에서 데이터를 가져온다.
•
매 조회시마다 새로운 스냅샷을 뜨기 때문에, 다른 트랜잭션이 커밋한 후 다시 조회하면 변경된 데이터를 볼 수 있다.
•
대부분의 데이터베이스 기본 격리 수준이며, REPEATABLE_READ와 함께 가장 많이 사용되는 방식이다.
•
Non-Repeatable Read 현상이 발생할 수 있다.
◦
트랜잭션에서 조회한 데이터가 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 변경되면, 다시 읽었을 때 새로운 값이 읽히며 데이터가 불일치하게되는 현상이다.
◦
하나의 트랜잭션 내에서 똑같은 SELECT 쿼리를 실행했을 때, 항상 같은 결과를 가져와야 한다는 REPEATABLE READ 정합성의 정의에 어긋난다.
▪
A트랜잭션이 데이터(row)를 읽음
▪
B트랜잭션이 같은 데이터를 수정하고 커밋
▪
A트랜잭션이 다시 같은 데이터를 읽었는데 데이터가 달라짐
REPEATABLE_READ
•
하나의 트랜잭션은 하나의 스냅샷만 사용한다.
•
A트랜잭션이 시작하고 처음 조회한 데이터의 스냅샷을 저장하고, 이후에 동일한 쿼리를 호출하면 스냅샷에서 데이터를 가져옵니다.
•
따라서, 중간에 B트랜잭션이 새로 커밋해도 A트랜잭션이 조회하는 데이터는 변하지 않는다.
•
Phantom Read라는 다른 트랜잭션에서 수행한 작업에 의해 안보였던 데이터가 보이는 현상이 발생할 수 있다.
◦
REPEATABLE_READ 격리 수준은 조회한 데이터에 대해서만 Shared Lock이 걸리기 때문에, 다른 트랜잭션이 새로운 데이터를 추가할 수 있다.
▪
A트랜잭션이 조회한 데이터는 0건
▪
B트랜잭션이 새로운 데이터를 추가하고 커밋
▪
A트랜잭션이 같은 쿼리로 다시 조회했더니 B트랜잭션이 추가한 데이터까지 같이 조회됨
SERIALIZABLE
•
가장 단순하고 엄격한 격리수준이다.
•
순차적으로 트랜잭션을 진행시키며, 읽기 작업에도 잠금을 걸어 여러 트랜잭션이 동시에 같은 데이터에 접근하지 못한다.
•
가장 안전하지만 성능 저하가 발생하기 때문에, 극도의 안정성을 필요로 하지 않으면 자주 사용되지 않는다
Propagation (전파레벨)
•
현재 진행중인 트랜잭션(부모 트랜잭션)이 존재할 때, 새로운 트랜잭션 메소드를 호출하는 경우 어떤 정책을 사용할 지에 대한 정의
◦
기존 트랜잭션 참여
◦
새로운 트랜잭션 생성
◦
non-transactional 상태로 실행 등
▪
트랜잭션은 존재하지만 커밋, 롤백이 되지 않는 상태
REQUIRED
•
디폴트 속성
•
부모 트랜잭션 내에서 실행하며(부모 트랜잭션이 존재할 경우 참여), 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성한다.
◦
자식 트랜잭션이 문제가 생기는 경우, 부모가 다 정상수행을 했다 하더라도 모두 롤백된다.
•
반대의 케이스인 부모 트랜잭션이 문제가 생기는 경우에도 모두 롤백된다.
SUPPORTS
•
이미 시작된 트랜잭션이 있으면 참여하고, 그렇지 않으면 트랜잭션 없이 진행하게 만든다. (non-transactional 상태로 실행)
MANDATORY
•
부모 트랜잭션이 있으면 참여하고, 없으면 예외 발생
◦
혼자서는 독립적으로 트랜잭션을 진행하면 안 되는 경우에 사용
REQUIRES_NEW
•
항상 새로운 트랜잭션을 시작한다. (부모 트랜잭션 무시)
•
이미 진행중인 트랜잭션이 있으면, 트랜잭션을 잠시 보류시킨다.
◦
자식 트랜잭션과 부모 트랜잭션은 각자 따로 커밋, 롤백
NOT_SUPPORTED
•
non-transactional 상태로 실행
•
이미 진행 중인 트랜잭션이 있으면 트랜잭션을 잠시 보류시킨다.
NEVER
•
트랜잭션을 사용하지 않도록 강제한다.
•
이미 진행 중인 트랜잭션도 존재하면 안된다. 있다면 예외를 발생시킨다. (부모 트랜잭션이 존재한다면 예외 발생)
NESTED
•
이미 진행중인 트랜잭션과 별개의 중첩 트랜잭션을 시작한다.
•
중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만드는 것이다.
◦
독립적인 트랜잭션을 만드는 REQUIRES_NEW와는 다르다.
•
부모 트랜잭션의 커밋과 롤백에는 영향을 받지만, 자신의 커밋과 롤백은 부모 트랜잭션에게 영향을 주지 않는다.
◦
NESTED의 핵심은 체크포인트이다. NESTED 호출 직전까지 일단 저장한다.
◦
자식 트랜잭션이 문제가 생겨 롤백이 되어도, 다시 체크포인트까지만 롤백이 된다. 따라서 부모는 커밋, 자식은 롤백 처리가 된다.
No-rollback-for
•
특정 예외가 발생하더라도 롤백되지 않도록 설정
출처: