출처 : https://www.youtube.com/watch?v=ImvYNlF_saE
데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위
상태를 변화시킨다는 것 ➡ SQL 질의어를 통해 DB에 접근한다는 것
작업 단위 ➡ 많은 SQL 명령문들을 사람이 정하는 기준에 따라 정하는 것
트랜잭션 없이 계좌이체를 결제를 한다면?
UPDATE acounts
SET balance = balance - 10000
WHERE user = '구매자';
UPDATE accounts
SET balance = balance + 10000;
WHERE user = '판매자';
여기서 오류가 발생한다면?
어떤 오류가있고, 어떻게 처리할지 이야기해보자
어중간한 상태로 두면 안된다. 전부 없었던 일로 해주자! (초기상태로 되돌리기)
이 일을 해주는 것이 바로 트랜잭션의 역할 (작업의 단위 all or nothing을 지정해주는 것)
위에서 나온 쿼리에 트랜잭션을 건 경우
BEGIN TRAN
UPDATE acounts
SET balance = balance - 10000
WHERE user = '구매자';
UPDATE accounts
SET balance = balance + 10000;
WHERE user = '판매자';
COMMIT TRAN
DMBS의 Storage System (저장 시스템)에 속하는 모듈 중 하나로, Main Memory에 유지하는 페이지를 관리하는 모듈
Buffer 관리 정책에 따라 UNDO 복구와 REDO 복구가 요구되거나, 그렇지 않게 되므로, 트랜잭션 관리에 매우 중요한 결정을 가져온다.
오퍼레이션 수행중에 수정된 page들이 Buffer 교체 알고리즘에 따라 디스크에 출력될 수 있음 (여기서 출력이란 memory에서 disk로 output연산이 되는 것을 말함)
Buffer 교체는 트랜잭션과는 무관하게 전적으로 Buffer의 상태에 따라서 결정된다.일관성 관점에 봤을 때는 임의의 방식으로 일어나게 된다.
즉, 아직 완료되지 않은 트랜잭션이 수정한 페이지들도 디스크에 출력될 수 있다. 따라서 만약 해당 트랜잭션이 어떤 이유든 정상적으로 종료될 수 없게 되면 트랜잭션이 변경한 (디스크에 출력된, 혹은 메모리 버퍼의) 페이지들은 원상 복구되어야 한다.
이러한 복구를 UNDO라고 한다.
만약 버퍼 관리자가 트랜잭션 종료 전에는 어떤 경우에도 수정된 페이지들을 디스크에 쓰지 않는다면, UNDO 연산은 메모리 버퍼에 되는 식으로 매우 간단해질 수 있다.
하지만, 이 부분은 매력적이지만 매우 큰 메모리 버퍼가 필요하다는 문제점을 가지고 있다.
수정된 페이지를 디스크에 쓰는 시점을 기준으로 아래의 두 개의 정책으로 나눌 수 있다.
steal 정책은 수정된 페이지가 어떠한 시점에도 디스크에 써질 수 있기 때문에 필연적으로 UNDO 로깅과 복구를 수반한다.
거의 모든 DBMS가 채택하는 버퍼관리 정책이다.
UNDO의 반대개념. 커밋한 트랜잭션의 수정은 어떠한 경우에도 유지(durability)되어야한다. 이미 커밋한 트랜잭션의 수정을 재반영하는 복구작업을 REDO라고 한다.
REDO 복구역시 버퍼관리정책에 영향을 받는다.
트랜잭션이 종료되는 시점에 해당 트랜잭션이 수정한 페이지들을 디스크에도 쓸 것인가 여부로 두 가지 정책이 구분.
여기서 주의할점
not force 정책이 수정했던 페이지(데이터)를 디스크에 반영하지 않느다는 점이지, 커밋 시점에 어떠한 것도 쓰지 않는다는 것은 아니다. 어떤 일들을 했었다고 하는 로그는 기록한다.
force 정책을 따르면 트랜잭션이 커밋되면 수정되었던 페이지들이 이미 디스크상의 데이터베이스에 반영되었으므로 REDO 복구가 필요없어진다.
반면 not force 정책을 따른다면 커밋한 트랜잭션의 내용이 디스크상의 데이터베이스상에 반영되어 있지 않을 수 있기 때문에 반드시 REDO 복구가 필요하다.
사실 force 정책을 따르더라도 데이터베이스 백업으로부터의 복구, 즉 미디어(media)복구 시에는 REDO 복구가 요구된다.
따라서 거의 모든 DBMS가 채택하는 정책은 not force이다.
DBMS는 버퍼 관리 정책으로 steal (언제든지 디스크에 변경사항이 출력될 수 있다) 과 not force (커밋시점에 디스크에 변경사항 기록을 강제하지 않는다.) 정책을 채택하고 있어, 이로 인해 REDO와 UNDO 복구가 모두 필요하다.
출처 : https://d2.naver.com/helloworld/407507
출처 :
https://www.youtube.com/watch?v=e9PC0sroCzc
https://jsonobject.tistory.com/467
https://n1tjrgns.tistory.com/266
트랜잭션이 시작하거나 참여하는 방법에 관한 설정
트랜잭션의 경계에서 트랜잭션이 어떻게 동작할 것인가
public class ServiceA {
private ServiceB serviceB;
...
@Transactional
public void a() {
service.b();
}
}
public class ServiceB {
@Transactional
public void b() {...}
}
위의 코드에서처럼 트랜잭션이 처리되는 과정안에서 또 다른 트랜잭션이 처리되는 경우가 있다.
부모 트랜잭션이 있냐 없냐에 따라 타입별로 트랜잭션의 경계를 설정할 수 있다.
@Transactional을 클래스 또는 메서드 레벨에 명시하면 해당 메서드 호출시 지정된 트랜잭션이 작동하게 된다. 해당 클래스의 Bean이 다른 클래스의 Bean에서 호출될 때만 @Transactional을 인지하고 작동하게 된다.
(같은 Bean 내에서 @Transactional이 명시된 다른 메서드를 호출해도 작동하지 않는다.)
스프링 프레임워크는 내부적으로 AOP를 통해서 해당 애노테이션을 인지하여 프록시를 생성해서 트랜잭션을 자동 관리한다.
스프링에서 제공하는 전파타입은 총 7가지
@Transactional(propagation = Propagation.REQUIRED)
public void doSomething() { ... }
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSomething() { ... }
@Transactional(propagation = Propagation.NESTED)
public void doSomething() { ... }
@Transactional(propagation = Propagation.MANDATORY)
public void doSomeThing() {...}
@Transactional(propagation = Propagation.SUPPORT)
public void doSomeThing() {...}
@Transactional(propagation = Propagation.NOT_SUPPORT)
public void doSomeThing() {...}
@Transactional(propagation = Propagation.NEVER)
public void doSomeThing() {...}
https://developyo.tistory.com/250 : 이게 제일 좋은 듯 (나중에 보기)
※ NESTED 사용의 예 :
메인 작업을 진행하며 이와 관련된 로그를 DB에 저장해야 한다.
로그를 저장하는 작업이 실패하더라도 메인 작업의 트랜잭션은 롤백하지 않는다.
하지만 메인 작업이 실패할 경우 로그 또한 저장하지 않아야 한다(롤백 되어야 한다).
Propagation 과 관련된 부분은 이곳을 참고