0 / 5
갱신 손실(Lost Update)
두 트랜잭션이 같은 데이터를 읽고 각자 고쳐 쓰면 나중에 쓴 값이 먼저 쓴 값을 덮어써 한쪽의 갱신이 사라진다.
두 판매의 연산이 섞여 실행되는 순서를 따라간다.
1 / 5
T1이 재고를 읽는다
T1이 재고 X를 읽는다. 지금 값은 10이다.
2 / 5
T2도 재고를 읽는다
T1이 아직 쓰기 전이라 T2도 10을 읽는다.
3 / 5
T1이 9를 쓴다
T1이 자기가 읽은 10에서 1을 빼 9를 쓴다.
4 / 5
T2가 T1의 값을 덮어쓴다
T2도 자기가 읽은 10에서 1을 빼 9를 쓴다. T1이 쓴 9를 모른 채 계산했으므로 T1의 갱신을 덮어쓴다.
5 / 5
결과: 갱신 손실
판매는 두 번인데 재고는 한 개만 줄었다.
원인:T2는 T1이 9를 쓰기 전에 읽은 10으로 계산했다.
1 / 4
T1이 9를 쓴다
T1이 X에 9를 쓴다. 결제를 확인하는 중이라 아직 Commit하지 않았다.
2 / 4
T2가 중간 값을 읽는다
T2가 확정되지 않은 9를 읽어 화면에 “재고 9개”를 보여 준다.
3 / 4
T1이 취소한다
결제가 실패해 T1이 Rollback한다. 재고는 다시 10이 된다.
4 / 4
결과: 현황 파악 오류
T2가 보여 준 9는 한 번도 확정된 적이 없는 값이다.
연쇄 복귀와 차이:여기서 문제는 확정되지 않은 값이 이미 화면에 나갔다는 점이다. 연쇄 복귀는 그 값을 읽은 트랜잭션까지 함께 Rollback해야 한다는 회복 쪽 문제다.
2 / 6
T2가 A에서 50을 뺀다
T2가 A에서 50을 뺀다. B에는 아직 더하지 않았다.
4 / 6
T2가 이체를 확정한다
T2가 Commit해 이체를 확정한다. 실제 총액은 그대로 200원이다.
5 / 6
T1이 B를 읽는다
T1이 B를 읽을 때는 이체가 이미 끝나서 B가 150이다.
6 / 6
결과: 모순성
T1이 읽은 A는 이체 전 값이고 B는 이체 후 값이다. 그래서 한 번도 있었던 적 없는 총액 250이 나왔다.
참고:이 예에서 DB에 저장된 값은 맞다. T1이 구한 총액만 앞뒤가 맞지 않는다.
1 / 7
T1이 9를 쓴다
T1이 X에 9를 쓴다. 아직 Commit하지 않았다.
3 / 7
T2가 9를 바탕으로 8을 쓴다
T2가 9에서 1을 빼 8을 쓴다. T1이 아직 Commit하지 않은 값으로 계산했다.
4 / 7
T1이 취소를 시작한다
결제가 실패해 T1이 Rollback해야 한다. 그런데 T1이 쓴 9 위에 T2가 이미 8을 썼다. T2부터 되돌려야 T1의 9를 되돌릴 수 있다.
5 / 7
T2도 취소된다
T2는 무효가 된 9로 계산했으므로 Rollback한다. DB는 T2가 쓴 8을 9로 되돌린다.
6 / 7
T1의 취소가 끝난다
DB가 T1이 쓴 9를 10으로 되돌린다. 그제야 처음 값 10이 된다.
7 / 7
결과: 연쇄 복귀
T1이 Rollback하자 T1이 쓴 값을 읽은 T2도 Rollback해야 했다. T2의 값을 읽은 T3가 있었다면 T3도 이어서 Rollback해야 한다.
현황 파악 오류와 차이:현황 파악 오류는 확정되지 않은 값을 읽은 결과가 문제이고, 연쇄 복귀는 그 값을 읽은 트랜잭션까지 줄줄이 Rollback해야 한다는 점이 문제다. 이 예에서는 T2가 쓴 8까지 되돌렸다.참고:T2가 이미 Commit했다면 T2는 Rollback할 수조차 없다. 이런 실행 순서를 회복 불가능 스케줄이라고 한다.