트랜잭션은 데이터베이스를 조작하는데 있어서 반드시 필요한 지식이다.
Insert, Update, Delete는 실행된 시점에서 반영이 된다.
Insert문을 실행하면 그 시점에서 Insert가 완료가 된다.
Update문을 실행하면 그 시점에서 Update가 완료된다.
그러나 트랜잭션을 쓰게 되면 한번에 반영할 수 있게 된다.
BEGIN을 붙인다. 그럼 트랜잭션이 시작된다.
트랜잭션 안에서는 결과가 바로 데이터베이스에 반영되지 않는다.
변경을 반영하기 위해서는 COMMIT라는 SQL문을 붙인다.
COMMIT을 실행한 시점에서 이 트랜잭션 안에서 실행한 갱신계 SQL이 한번에 반영된다.
COMMIT이 아닌 ROLLBACK을 입력하면 전부 취소가 된다.
한번 COMMIT 한 건을 ROLLBACK 하는 것은 안되므로 주의가 필요하다.
왜 트랜잭션이 필요한지 생각해보자.
EC 사이트의 구입 처리를 예로 보자.
EC 사이트의 구입 처리는 이런 흐름이다.
1. 재고 테이블의 업데이트
2. 크레디트카드 결제 처리(API)
3. 구입이력의 테이블의 삽입
상품이 구입되면 그 상품의 재고를 줄여야 한다.
재고 테이블에 업데이트 문을 친다.
う-つ [打つ] (키보드를) 치다, 두드리다.
그 다음 크레디트 카드의 결제처리를 행한다. 크레디트 카드가 준비해준 api를 실행하는 것을 상정.
데이터베이스와 얽힌 처리가 아니다.
からむ(絡む) 얽히다.
크레디트 카드의 결제가 성공하면, 구입이력테이블에 Insert한다.
트렌젝션이 없는 경우
재고를 줄이기 위해 재고 테이블을 업데이트 한다.
이 시점에서 재고가 반영이 된다.
그 다음 크레디트 카드 결제 철가 발생한다. 이 때 크레디트 카드 결제가 실패하면 어떻게 되는가?
결제가 실패했어도 이미 Update가 되어버려서 재고가 줄어있는 상태이다.
이렇게 재고 부정합이 발생하지 않도록 일련의 처리를 일괄로 처리하도록 한다. 그렇기 때문에 트랜젝션이 필요하다.
ひとくくり(一くくり·一括り) 한데 묶음, 일괄
트랜젝션을 사용하는 경우
BEGIN을 실행합니다.
이것으로 트랜젝션이 개시가 된다.
다음에 재고 테이블의 업데이트를 행한다.
이번에는 트랜젝션의 과정중이므로 바로 실행되지는 않는다.
그 다음에는 크레디트카드의 결제를 실행한다.
여기서 실패를 하게 되면 롤백이 발생한다.
여기서 롤백의 커멘드가 쳐지면 아까의 업데이트 분은 취소가 된다.
다음은 트랜젝션이 성공하는 시나리오를 보자.
BEGIN을 실행한다.
여기서 트랜젝션이 개시가 된다.
트랜젝션의 안에서 재고테이블이 업데이트 된다.
트렌젝션 과정 중이므로 이 시점에서 아직 반영은 되지 않는다.
그 다음 크레디트 카드 결제 처리를 한다.
해당 처리가 올바르게 일어났다고 가정하자.
마지막으로 구입이력 테이블에 Insert를 하도록 하자.
여기까지 모든 처리가 성공을 했다면, COMMIT이 실행된다.
그러면 트랜젝션이 완료가 된다.
UPDATE/INSERT가 한번에 처리된다.
트렌젝션을 쓰면 일련의 처리를 한번에 가능하다.
도중에 실패해도 롤백해서 없었던 것처럼 한다.
트랜잭션은 언제 필요한가?
수동으로 SQL코멘드를 조작할 때.
복수 데이터의 정합성을 보존할 때.
운용업무의 실제DB에 SQL을 흘릴 때 이다.
실제 환경의 데이터베이스를 갱신하는 것이므로, 잘못한 SQL을 흘리면 안된다.
트랜잭션을 해서 작업을 일으킨다.
롤백을 하면 없었던 것으로 할 수 있다.
EC사이트의 예처럼, 복수 테이블이나 복수 레코드를 한번에 처리할 때이다.
2개의 터미널을 써서 SQL조작을 해보겠다.
users의 테이블을 불러왔지만 지금은 아무것도 없다.
begin으로 트랜잭션을 개시.
insert문을 실행한다.
1건의 인서트가 실행되었다.
오른쪽에 실행해보니 아무 것도 없다.
같은 SQL문인데 왼쪽에는 1건의 데이터가 존재하는데, 오른쪽에는 데이터가 존재하지 않는다.
왜냐하면 왼쪽은 Begin으로 트랜잭션의 과정 중에 있다.
그래서 Commit 전에는 반영되지 않는다.
왼쪽은 Commit 전의 가등록 상태이다.
트랜잭션을 실행시킨 자기 자신에게는 보이나 다른 사람에게는 보이지 않는다.
Commit완료로 정식적으로 해당 데이터가 반영되었다.
Commit을 행했기 때문에, 데이터가 확정되어서 오른쪽에서도 참조될 수 있게 되었다.
트랜잭션에서 Update와 Delete도 해볼 것이다.
트랜잭션 개시 후,
이름 A였던 사용자 이름을 A2로 변경.
그리고 조회해보니 바뀐 것으로 보인다.
오른쪽의 이름은 A 그대로 이다.
왼쪽의 터미널에서는 트랜잭션 안에서 업데이트 문을 실행해서 오른쪽과 같이 되었다.
오른쪽에서 보이는 A의 정보는 begin 개시 전의 데이터이다.
오른쪽에서는 2건의 insert문을 실행.
3개의 데이터가 있다.
그러나 오른쪽에서는 아직도 그대로 1건의 이름 변경전 데이터가 있다.
A4 사용자를 지우고 조회하면 삭제가 된 상태로 보이지만, 아직 오른쪽에서는 반영되지 않았다.
ROLLBACK을 실행.
복수의 Insert, Update, Delete 등 전부 취소되었다.
트랜잭션 전의 상태로 되 돌아옴.
양쪽에 트랜잭션을 걸어서 데이터의 변경을 해보도록 하겠다.
양쪽에 begin을 걸었다.(트랜잭션)
왼쪽에서 update문을 입력하여 A의 이름을 A3로 변경하면 처리가 됐다고 나오지만,
오른쪽에서 같은 작업을 하고자 했는데 반응이 오지 않는다.
서로 다른 트랜잭션 이지만, 같은 데이터에 대해 변경을 했기 때문에
데이터의 부정하을 막기 위해 왼쪽의 트랜잭션이 종료될 때 까지
다른 트랜잭션은 순번 기다리기를 한다.
에러가 나온 것처럼 Lock Wait Timeout 이 된다.
기다리던 처리는 강제 종료된다.
아직 왼쪽이 대기중(트랜잭션 중)이므로 기다리고 있다.
커밋을 입력한 순간에 오른쪽에 처리 결과가 나온다.
한쪽이 트랜잭션이 진행되면 락이 걸린다.
오른쪽 터미널은 기다리는 상황이 된다.
순번 기다리던 오른쪽은 왼쪽이 commit되면 실행이 된다.
뭔가 위험하다.
EC사이트에서 재고가 1 -> 0 되는 상황을 주목.
최종 1개의 재고를 update문.
왼쪽에서 commit 완료 후 재고가 0이 되었다.
그 후 오른쪽의 재고 update문이 실행이 된다.
재고가 1개인데 양쪽다 재고를 확보한 상황처럼 된다.
데이터의 모순을 없애기 위해서는 락에 대해 더 배워야 한다.
댓글 영역