데이터베이스의 상태를 변환시키는 하나의 논리적인 작업 단위를 구성하는 연산들의 집합이다.
예를들어, A계좌에서 B계좌로 일정 금액을 이체한다고 가정하자.
- A계좌의 잔액을 확인한다.
- A계좌의 금액에서 이체할 금액을 빼고 다시 저장한다.
- B계좌의 잔액을 확인한다.
- B계좌의 금액에서 이체할 금액을 더하고 다시 저장한다.
이 과정들이 모두 합쳐서 계좌이체라는 하나의 작업단위(트랜잭션)를 구성한다.
데이터 베이스 응용프로그램은 트랜잭션의 집합으로 정의할 수 있다.
하나의 트랜잭션은 Commit 되너가 Rollback 된다.
한개의 논리적 단위(트랜잭션)에 대한 작업이 성공적으로 끝나
데이터베이스가 다시 일관된 상태에 있을 때,
이 트랜잭션이 행한 갱신 연산이 완료된 것을 트랜잭션 관리자에게 알려주는 연산이다.
하나의 트랜잭션 처리가 비정상적으로 종료되어 데이터베이스의 일관성을 깨뜨렸을 때,
이 트랜잭션의 일부가 정상적으로 처리되었더라도
트랜잭션의 원자성을 구현하기 위해 이 트랜잭션이 행한 모든 연산을취소(Undo)
하는 연산이다.Rollback 시에는 해당 트랜잭션을 재시작하거나 폐기한다.
트랜잭션이 실행 중에 있는 상태, 연산들이 정상적으로 실행 중인 상태
트랜잭션이 실행에 오류가 발생하여 중단된 상태
트랜잭션이 비정상적으로 종료되어 Rollback 연산을 수행한 상태
트랜잭션이 마지막 연산까지 실행했지만, Commit 연산이 실행되기 직전의 상태
트랜잭션이 성공적으로 종료되어 Commit 연산을 실행한 후의 상태
- 현금 인출기를 작동하는 도중에 기계 오류나 정전 등과 같은
예기치 않은 상황이 발생하여 카드가 나오지 않거나 기계가 멈추는 경우- 각각 다른 지점의 은행에서 동시에 인출할 때,
하나의 지점이 다른 지점에서 저장한 잔액을 덮어 쓰는 경우위와 같은 상황이 발생되지 않도록 방지하기 위해,
즉, 트랜잭션의 성질인 ACID를 제공받기위해 트랜잭션을 사용한다.
트랜잭션의 모든 연산들은 정상적으로 수행 완료되거나
아니면 전혀 어떠한 연산도 수행되지 않은 상태를 보장해야 한다.
트랜잭션 완료 후에도 데이터베이스가 일관된 상태로 유지되어야 한다.
하나의 트랜잭션이 실행하는 도중에 변경한 데이터는
이 트랜잭션이 완료될 때까지 다른 트랜잭션이 참조하지 못한다.
성공적으로 수행된 트랜잭션은 영원히 반영되어야 한다.
트랜잭션의 성질인 ACID 원칙을 엄격하게 지키면
동시성 처리 성능이 떨어진다.이런 문제점을 해결하기 위해서 4가지 단계로 구분한다.
아래 단계일수록 엄격한 단계이며 동시성이 떨어진다.
트랜 잭션 처리중인 혹은 아직 커밋되지 않은 데이터를
다른 트랜잭션이 읽는 것을 허용한다.B 트랜잭션이 "훈이" 라고 이름을 변경하고 Commit 하려는 찰나에 읽었는데
"신짱구" 가 아닌 "훈이"를 Read 하였다.
DIRTY READ
커밋되지 않은 수정 중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할 때 발생하는 현상
어떤 트랜잭션에서 아직 실행이 끝난지 않은 다른 트랜잭션에 의한 변경 사항을 보게 되는 되는 경우B 트랜잭션이 Commit 되지 않은 트랜잭션 A 변경사항을 읽어 갔는데
A가 RollBack 되었다.RollBack 이름 "훈이" 는 데이터베이스에 존재하지 않게되는데
트랜잭션은 "훈이" 를 읽어가면서 부정합이 발생한다.
Commit이 이루어진 트랜잭션만 조회할 수 있다.
SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸리는 단계
B 트랜잭션은 Select를 두번 하고 COMMIT 한다.
처음 Select는 A 트랜잭션이 시작되지 전이여서 "신짱구" 를 읽어온다.두번째 Select는 A 트랜잭션에서 "신짱구"를 "훈이" 로 변경하고 그 이후 이다.
READ UNCOMMITED 는 변경된 테이블에 바로 접근해서 "훈이"를 읽지만
READ COMMITED는 "UNDO" 영역인 변경되기전 값을 저장하는 영역에 접근하여 값을 읽는다.
NON-REPEATABLE READ
하나의 트랜잭션 내에서 같은 조회를 했을 때 반드시 같은 결과값을 반환해야한다는 정합성이다.
위 그림 처럼 하나의 트랜잭션에서 같은 조회를 했을때 다른 결과값을 읽어오는 문제를 말한다.
모든 트랜잭션은 트랜잭션 ID 를 가지게 된다.
자신보다 낮은 ID의 트랜잭션의 ID 에서만 참조하여 값을 읽는다.
A,B 트랜잭션이 있기전에 "신짱구"를 만들어낸 트랜잭션의 ID는 3이다.
B의 트랜잭션 ID는 5이고 A의 트랜잭션 ID는 7 이다.B 트랜잭션에서 조회시 자신 보다 낮은 id(3) 의 트랜잭션이 수정한 "신짱구" 를 읽는다.
id 값이 7인 B 트랜잭션이 "신짱구"를 "훈이"로 변경한다.
A 트랜잭션(5)이 다시 그값을 읽으려고 할때 B의 트랜잭션 id값(7)은 자신보다 크므로
이 변경사항은 읽지 못한다.
따라서 "신짱구"를 읽어오게 된다.
PHANTOM READ
같은 트랜잭션 내애서 같은 조회를 두번 이상했을 때
기존에 없던 데이터가 추가되어서 나오는 현상 또는 문제점"FOR UPDATE" 절은 선택한 행에 대해 독점적인 잠금을 설정하려고 시도한다.
이는 다른 트랜잭션이 해당 행을 수정하거나 삭제하지 못하게 하여 동시성 문제를 방지한다.
이 잠금은 트랜잭션이 커밋되거나 롤백될 때까지 유지됩니다.그러나 Insert 구문은 UNDO 영역에 잠금을 설정할 수 없다.
그렇기 때문에 UNDO 영역이 아니라 실제 데이터 베이스에 접근하기 때문에
이와 같은 현상이 발생한다.
가장 엄격한 격리 수준으로
한 트랜잭션이 끝나기전까지 다른 트랜잭션은 해당 트랜잭션이 끝날때 까지 데이터에 접근할 수 없다.모든 부정합 문제를 해결한다.
동시 처리가 불가능하기 때문에 권장하지 않는다.
참고인데 JPA 에서는 데이터베이스 격리 수준을
READ COMMITED
로 간주한다.
만약 더 높은 수준의 격리를 적용하고 싶다면 비관적 락과, 낙관적 락을 사용하게 된다.