트랜잭션이란?

  • 단일한 논리적인 작업 단위
  • 논리적인 이유로 여러 SQL문들을 단일 작업으로 묶어서 나눠질 수 없게 만든 것이 트랜잭션
  • 트랜잭션의 SQL문들 중에 일부만 성공해서 DB에 반영되는 일은 일어나지 않는다

예를 들어 이체 작업이 일어날 경우, A계좌에서 B계좌로 돈이 이동되는 작업을 수행하기 위해 각 계좌마다 SQL문이 실행되어 계좌의 상태를 변경할 수 있습니다. 각 SQL문은 모두 성공해야 하며, 하나라도 실패되었을 경우, 이체 작업이 성공했다고 말할 수 없습니다. 이러한 여러 동작을 단일 작업으로 묶은 것을 트랜잭션이라고 합니다.

TCL

Transaction Control Language로 트랜잭션을 제어하는 언어입니다. 주로 COMMIT, ROLLBACK 및 SAVEPOINT 등의 명령문으로 구성됩니다.

-- 예시를 위한 테이블 생성
CREATE TABLE accounts (
    account_number VARCHAR2(20) PRIMARY KEY,
    balance NUMBER
);

-- 계좌 생성
INSERT INTO accounts (account_number, balance) VALUES ('1234567890', 10000);
INSERT INTO accounts (account_number, balance) VALUES ('0987654321', 5000);

이러한 테이블이 있다고 가정했을 때, 어떠한 작업을 수행하기 위해 여러 SQL문을 수행해야 한다면 SQL문들을 하나의 트랜잭션으로 정의해 동작하게 할 수가 있습니다.

위 SQL문은 ’계좌 이체’ 라는 작업을 트랜잭션으로 관리하기 위해 계좌 테이블을 생성하고, 2개의 계좌 정보가 담긴 데이터를 생성하였습니다

-- 계좌 이체 트랜잭션
BEGIN
    DECLARE
        sender_account VARCHAR2(20) := '1234567890';  -- 송신 계좌
        receiver_account VARCHAR2(20) := '0987654321';  -- 수신 계좌
        transfer_amount NUMBER := 2000;  -- 이체 금액

        sender_balance NUMBER;
        receiver_balance NUMBER;
    BEGIN
        -- 송신 계좌 잔액 확인
        SELECT balance INTO sender_balance FROM accounts WHERE account_number = sender_account FOR UPDATE;
        
        -- 송신 계좌의 잔액이 이체 금액보다 적은 경우 롤백
        IF sender_balance < transfer_amount THEN
            RAISE_APPLICATION_ERROR(-20001, 'Insufficient funds in sender account');
        END IF;
        
        -- 송신 계좌에서 이체 금액 차감
        UPDATE accounts SET balance = balance - transfer_amount WHERE account_number = sender_account;
        
        -- 수신 계좌에 이체 금액 추가
        UPDATE accounts SET balance = balance + transfer_amount WHERE account_number = receiver_account;
        
        -- 트랜잭션 커밋
        COMMIT;
    EXCEPTION
        WHEN OTHERS THEN
            -- 오류 발생 시 롤백
            ROLLBACK;
            DBMS_OUTPUT.PUT_LINE('Error occurred: ' || SQLERRM);
    END;
END;

위의 예시는 계좌 이체를 트랜잭션으로 처리하는 Oracle PL/SQL 코드입니다.

먼저 두 개의 계좌가 있는 가상의 테이블을 생성하고, 송신 계좌에서 수신 계좌로 일정 금액을 이체하는 트랜잭션을 구현합니다. 송신 계좌의 잔액이 이체 금액보다 적을 경우에는 에러를 발생시키고 롤백됩니다. 그렇지 않으면 송신 계좌에서는 이체 금액을 차감하고 수신 계좌에는 이체 금액을 추가한 후에 트랜잭션을 커밋합니다. 만약 어떤 이유로든 오류가 발생하면 롤백됩니다.

여기서 TCL인 COMMIT과 ROLLBACK이 나오는데, COMMIT은 지금까지 작업한 내용을 DB에 영구적으로(permanently) 저장하라는 의미를 가지고 있습니다. 그리고 동시에 트랜잭션을 종료한다라는 의미도 있습니다.

ROLLBACK은 지금까지의 작업들을 모두 취소하고 트랜잭션 이전 상태로 되돌리는 의미를 가지고 있습니다. 그리고 동시에 트랜잭션을 종료한다라는 의미도 COMMIT과 똑같이 가지고 있습니다.


위 예시와 같이 일반적으로 트랜잭션은 다음과 같은 사용 패턴을 보입니다.

  1. 트랜잭션을 시작(begin)한다.
  2. 데이터를 읽거나 쓰는 등의 SQL문들을 포함해서 로직을 수행한다.
  3. 일련의 과정들이 문제없이 동작했다면 트랜잭션을 COMMIT한다.
  4. 중간에 문제가 발생했다면 트랜잭션을 ROLLBACK한다.

원자성 Atomicity

  • All or Nothing
  • 트랜잭션의 SQL문은 전부 처리되거나 취소되어야 한다

→ 트랜잭션 내의 모든 작업은 전체적으로 하나의 단위로 간주되어야 하며, 이 단위에서 어떠한 부분도 분리되거나 따로 처리될 수 없습니다. 따라서 트랜잭션 내에서 하나 이상의 작업이 실패하면, 전체 트랜잭션은 롤백되어 이전 상태로 되돌아가야 합니다.

일관성 Consistency

  • 트랜잭션 실행의 결과로 데이터베이스 상태가 모순되지 않아야 한다

→ 트랜잭션이 완료된 후에도 데이터베이스는 미리 정의된 규칙이나 제약 조건을 준수하여 일관된 상태로 있어야 한다는 것을 말합니다. 예를 들어 이체 프로세스 수행시 이체하려는 돈보다 계좌에 있는 돈이 더 적을 경우 어떠한 제약 조건이 걸려있을 것이고, 제약 조건을 위배하기 때문에 트랜잭션은 실패되야만 합니다.

격리성 Isolation

  • 실행 중인 트랜잭션의 중간결과를 다른 트랜잭션이 접근할 수 없다.

→ 동시에 여러 트랜잭션이 실행될 때 각 트랜잭션이 다른 트랜잭션의 작업에 영향을 미치지 않고 독립적으로 실행되어야 함을 의미합니다. 다시 말해, 하나의 트랜잭션이 실행 중일 때 그 트랜잭션의 결과가 완전히 적용되기 전까지는 다른 트랜잭션에서 해당 결과를 볼 수 없어야 합니다.

영속성 Durability

  • 트랜잭션이 일단 그 실행을 성공적으로 완료하면 그 결과는 데이터베이스에 영속적으로 저장된다.
  • commit된 트랜잭션은 DB에 영구적으로 저장한다.
  • DBMS가 보장함 → 개발자가 따로 설정을 해줄 필요 없음

→ 트랜잭션이 성공적으로 완료되고 커밋되면 해당 트랜잭션에서 수행된 모든 변경 사항이 영구적으로 저장되고 시스템이 고장 나더라도 유지되어야 함을 의미합니다.

스케줄 Schedule

  • 여러 transaction들이 동시에 실행될때 각 transaction에 속한 operation의 실행 순서
  • 각 transaction 내의 operation들의 순서는 바뀌지 않는다

Serial schedule

  • transaction들이 겹치지 않고 한번에 하나씩 실행되는 schedule
  • Nonserial schedule은 그 반대
  • 이상한 결과를 만들 가능성 x
  • 좋은 성능을 낼 수 없고 현실적으로 사용할 수 없는 방식
  • Nonserial schedule은 겹쳐서 실행되기 때문에 동시성이 높아져서 같은 시간 동안 더 많은 트랜잭션을 처리할 수 있다.

Nonserial로 실행해도 이상한 결과가 나오지 않는 방법 → equivalent

Conflict of two operations

  • 세 가지 조건을 모두 만족하면 conflict
    • 서로 다른 transaction 소속
    • 같은 데이터에 접근
    • 최소 하나는 write operation

Untitled

스케줄이 동일하다 → Conflict equivalent

Conflict equivalent for two schedules

  • 두 조건 모두 만족하면 Conflict equivalent
    • 두 스케줄은 같은 transaction을 가진다
    • 어떤 conflicting operations의 순서도 양쪽 schedule 모두 동일하다

Conflict serializable

serial schedule와 conflict equivalent일 때

고민거리 : 성능 때문에 nonserial schedule로 만들지만 이상한 결과가 나오지 않았으면 좋겠다

→ conflict serializable한 nonserial schedule을 허용하자!

Untitled


Untitled

unrecoverable schedule

schedule 내에서 commit된 transaction이 rollback된 transaction이 write했었던 데이터를 읽은 경우

이전 상태로 회복 불가능 → DBMS가 허락하면 안됨

recoverable schedule

  • 의존성이 발생하면 의존대상이 COMMIT/ROLLBACK 하기전까지는 COMMIT하지 않는다
  • 의존성이 일어난 두 트랜잭션을 롤백하는것 → Cascading rollback
  • Cascading rollback은 처리비용 많이 듦 → 문제를 어떻게 해결할까?
  • 트랜잭션 끝날때마다 commit/rollback 해주면 됨 → cascadeless schedule
  • commit되지 않은 transaction들이 write한 데이터는 읽지 않는 경우
  • strict schedule

Untitled

isolation level

Dirty read

dirty read란 commit되지 않은 변화를 읽었을 경우를 말합니다.

  • rollback을 하지 않아도 발생 가

Non-repeatable read(Fuzzy read)

같은 데이터의 값이 한 트랜잭션 내에서 달라질 경우를 말합니다.

Phantom read

없던 데이터가 생기는 것을 말합니다. Fuzzy read와 같이 한 트랜잭션 내에서 특정 데이터를 2번 읽는데 1번 읽을때 없던 데이터가 다른 트랜잭션에서 새로 write되어서 2번 읽을때 갑자기 생기는 경우를 말합니다.

이런 이상한 현상들이 모두 발생하지 않게 만들 수 있지만 그러면 제약사항이 많아져서 동시 처리 가능한 트랜잭션 수가 줄어들어 결국 DB의 전체 처리량이 하락하게 된다.

일부 이상한 현상은 허용하는 몇 가지 level을 만들어서 사용자가 필요에 따라서 적절하게 선택할 수 있도록 하자!(isolation level)

Untitled

세가지 이상 현상을 정의하고 어떤 현상을 허용하는지에 따라서 각각의 isolation level이 구분됩니다. 그렇기 때문에 애플리케이션 설계자는 isolation level을 통해 전체 처리량과 데이터 일관성 사이에서 어느 정도 거래를 할 수 있다.

다른 이상 현상들

dirty write

commit 안된 데이터를 write 할 경우 생기는 문제입니다. → rollback 시 정상적인 recovery는 매우 중요하기 때문에 모든 isolation level에서 dirty write를 허용하면 안된다.

lost update

업데이트를 덮어 썼을 때 발생하는 현상입니다.

Read skew

commit된 데이터를 읽었지만 inconsistent한(일관성 없는) 데이터를 읽었을 경우 발생합니다.

write skew

서로 다른 데이터에 다른 트랜잭션끼리 write를 실행했어도 inconsistent한 데이터 쓰기가 발생하는 경우를 말합니다.

Categories:

Updated:

Leave a comment