이번에는 트랜잭션에 대한 개념을 정립하고 트랜잭션의 격리성으로 인해 발생할 수 있는 문제점과 해소 방법에 대해 알아본다.
데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 최소 단위이다.
즉, 데이터베이스에 여러 개의 작업(INSERT, UPDATE, DELETE 등)을 묶어서 모두 성공하면 반영하고, 하나라도 실패하면 전부 취소하는 것을 의미한다.
BEGIN;
-- 주문 테이블에 새로운 주문 추가
INSERT INTO orders(user_id, product_id, quantity) VALUES (1, 101, 2);
-- 상품 재고 차감
UPDATE products SET stock = stock - 2 WHERE product_id = 101;
-- 결제 내역 기록
INSERT INTO payments(order_id, amount, status) VALUES (LAST_INSERT_ID(), 50000, 'SUCCESS');
COMMIT;
위와 같이 상품을 주문한 뒤 상품 재고를 차감하고 결제 내역을 기록하는 트랜잭션이 있다고 생각해보자.
만약 주문은 발생했지만 상품 재고의 차감하는 과정에서 오류가 발생한다면 돈만 결제되고 재고는 안 줄어드는 불상사가 발생하고 이 데이터는 믿을 수 없는 데이터가 된다.
이를 해결하기 위한 방법으로 DB는 All Or Nothing 전략을 취한다.
즉, 모든 작업을 성공적으로 수행하거나 작업을 수행하기 전으로 RollBack을 한다.
트랜잭션의 수행 중간에 다른 트랜잭션이 간섭을 할 수 없다면 당연히 모든 트랜잭션은 순차적으로 처리될 것이며 데이터의 정합성을 보장할 수 있을 것이다. 하지만 이럴 경우 트랜잭션을 처리하는 동안 다른 트랜잭션들은 데이터에 접근할 수 없으며 그만큼 대시시간이 증가하여 성능 저하로 이어질 것이다.
그렇기 때문에 사용자에게 불편함없이 처리 속도를 유지하기 위해서는 완전한 격리보다는 적당한 격리가 필요하다.
하지만 완전한 격리를 하지 않을 경우 다음과 같은 문제가 발생한다.
완전한 격리가 아닌 어느 정도 허용된 격리를 하여 다른 트랜잭션이 간섭을 하게 될 경우 다음과 같은 격리성 문제(Dirty Read, Non-Repetable Read, Phantom Read)가 발생할 수 있다.
다른 트랜잭션으로 데이터가 수정되었지만 커밋되지 않은 상태에서 데이터를 조회하는 경우를 말한다.
다른 트랜잭션에서 커밋을 할지 롤백을 할지 알 수 없기 때문에 문제가 발생한다.

하나의 트랜잭션(T1)이 데이터를 두 번 읽었는데, 중간에 다른 트랜잭션(T2)이 그 데이터를 수정하거나 삭제해버리면 읽는 값이 달라지는 현상을 말한다.
이는 다른 트랙잭션에서 데이터의 값을 수정하였기 때문이다.

한 트랜잭션 내 같은 조건으로 조회를 했을 때 행의 개수가 달라지는 현상을 말한다.
이는 다른 트랜잭션에서 데이터를 추가하거나 삭제하였기 때문이다.

DBMS에서는 위 문제를 제어하기 위해 격리수준을 4단계로 나눈다.
트랜잭션에서 처리중이지만 아직 커밋되지 않은 데이터를 다른 트랜잭션에서 읽는 것을 허용한다. 해당 수준에서는 Dirty Read, Non-Repeatable Read, Phantom Read가 일어날 수 있으며, 정합성 문제가 발생할 수 있다.
트랜잭션의 커밋이 확정된 데이터만 다른 트랙잭션에서 읽을 수 있도록 허용한다. 커밋 되지 않은 데이터에 대해서는 실제 DB 데이터가 아닌 Undo 로그에 있는 이전 데이터를 가져온다. 해당 수준에서는 여전히 Non-Repeatable Read, Phantom Read가 발생할 수 있다.
앞서 Non-Repeatable Read는 하나의 트랜잭션(T1)이 데이터를 두 번 읽었는데, 중간에 다른 트랜잭션(T2)이 그 데이터를 수정하거나 삭제해버리면 읽는 값이 달라지는 현상이라고 했다.
Repeated Read에서는 T2에서의 데이터 수정 혹은 삭제가 발생할 경우 Undo 로그에 기존 데이터를 저장해두고 T1에서 데이터를 읽을려고 할 때 Undo 로그에 저장된 백업 데이터를 가져와 사용한다.
이렇게하면 삭제와 수정에 대해서 트랜잭션내에서 불일치를 가져오던 Non-Reapeatable Read를 해소할 수 있습니다.
트랜잭션 내에서 쿼리를 두 번 이상 수행할 때, 첫번째 쿼리에 있던 결과에 어떠한 변경이나 추가가 나타나지 않도록 한다.
즉, 항상 동일한 쿼리 결과를 보여준다.