혹시 '데이터 베이스를' 아십니까 ? #4 – 트랜잭션

전하윤·2025년 7월 7일
0

DB

목록 보기
4/7
post-thumbnail

목차


개요

앞선 글에서는 정규화(Normalization)라는 설계 기법을 통해
데이터의 중복과 불일치, 이상(anomaly) 현상을
효과적으로 방지할 수 있음을 확인했다.

하지만, 실제로 여러 사용자가 동시에 데이터를 읽고,
수정하는 “실시간 운영 환경”에서는
단순한 설계만으로는 데이터의 ‘정확성’과 ‘일관성’
완전히 보장하기 어렵다.

엑셀이나 스프레드시트처럼 단순한 파일에서는
동시에 여러 사용자가 접근할 경우
데이터가 꼬이거나, 예기치 않은 충돌이 빈번하게 발생할 수 있다.

예를 들어,
“송금”이나 “재고 관리” 같은 중요한 비즈니스 로직에서
하나의 데이터가 여러 작업에 의해 동시에 변경된다면
잘못된 금액이 이체되거나,
재고가 음수로 떨어지는 심각한 문제가 생길 수 있다.

이런 문제를 해결하기 위해
데이터베이스는 트랜잭션(Transaction)이라는
강력한 보호장치를 제공한다.

이번 글에서는
트랜잭션이란 무엇이고, 왜 필요한지,
그리고 실전에서 어떤 원리와 규칙으로
데이터의 신뢰성을 지키는지
쉽고 구체적으로 정리해보려 한다.


트랜잭션(Transaction)이란?

트랜잭션(Transaction)의 사전적 의미는 거래이고,
컴퓨터 과학 분야에서의 트랜잭션(Transaction)은 "더이상 분할이 불가능한 업무처리의 단위"를 의미한다.

이것은 하나의 작업을 위해 더이상 분할될 수 없는 명령들의 모음,
즉, 한꺼번에 수행되어야 할 일련의 연산모음을 의미한다.

우리가 사용하는 DBMS에서 트랜잭션은 데이터베이스를 상태를 바꾸는 일종의 작업 단위이다.

INSERT, DELETE, UPDATE 등의 SQL 명령문을 통해 데이터를 상태를 바꾸는 일련의 모든 과정이 하나의 트랜잭션이다. 이때 오해하면 안되는게,각각 명령어가 하나의 트랜잭션이 될 수도 있지만, 묶여서 하나의 트랜잭션이 될 수도 있는 것이다.


트랜잭션의 4가지 특성(ACID)

트랜잭션의 4가지 특성

  • Atomicity(원자성)
  • Consistency(일관성)
  • Isolation(격리성)
  • Durability(영속성)

Atomicity(원자성)

원자성은 트랜잭션이 데이터베이스에 모두 반영되던가, 아니면 전혀 반영되지 않아야 한다는 것이다.

  • 트랜잭션 내의 모든 작업이 “한 덩어리”로서 수행된다.

  • 중간에 한 단계라도 실패하면, 그 전에 진행된 모든 변경 사항도 함께 롤백(취소)된다.

  • 원자성의 특성을 반영하면 데이터가 꼬이는 일을 방지 할 수 있다.

Consistency(일관성)

일관성은 트랜잭션의 작업 처리 결과가 항상 일관성이 있어야 한다는 것이다.

  • 트랜잭션 전/후의 데이터가 무결성(Integrity), 제약조건(Constraints), 비즈니스 규칙 등을 항상 지켜야 함을 의미합니다.

  • 잘못된 트랜잭션은 아예 실행되지 않거나, 실행 도중 롤백됩니다.

Isolation(격리성)

독립성은 둘 이상의 트랜잭션이 동시에 실행되고 있을 경우,
어떤 하나의 트랜잭션이라도 다른 트랜잭션의 연산에 끼어들 수 없다는 점을 가리킨다.

  • 한 트랜잭션이 완료되기 전까지 다른 트랜잭션이 그 변경사항을 볼 수 없거나, 영향을 받지 않아야 함을 의미합니다.

  • 각각의 트랜잭션이 “내부적으로는 단독으로 실행되는 것”처럼 느껴져야 합니다.

  • 격리 수준(4단계)마다 세부 동작이 다르지만, 기본 개념은 서로 간섭 불가능 합니다.

Durability(영속성)

커밋된 트랜잭션의 결과는 데이터베이스에 영구적으로 저장되어야 한다.

  • 일단 트랜잭션이 커밋되면, 정전, 서버 다운, 시스템 장애가 발생해도 해당 데이터는 영원히 보존되야 합니다.

  • 대부분의 데이터베이스는 커밋 시 디스크 기록(Write Ahead Logging 등)까지 완료한 뒤
    성공 신호를 줍니다.


트랜잭션의 상태와 생명주기

트랜잭션은 작업을 시작해서 끝날 때까지 여러 상태를 거치는데, 대표적인 상태 변화는 아래와 같다.

상태설명
활성 (Active)트랜잭션이 시작되어 현재 연산(삽입, 삭제, 갱신 등)이 진행 중인 상태. 아직 아무것도 확정되지 않은 “진행 중” 단계.
부분 완료 (Partially Committed)트랜잭션의 마지막 연산이 끝난 상태. 하지만 완전히 DB에 기록(Commit)되진 않았으며, DBMS가 내부적으로 변경사항을 점검 중인 단계.
완료 (Committed)트랜잭션의 모든 작업이 성공적으로 처리되어 영구적으로 데이터베이스에 반영된 상태. 더 이상 롤백이 불가능함.
실패 (Failed)트랜잭션 처리 도중 오류(예: 제약조건 위반, 시스템 오류)가 발생하여 더 이상 진행이 불가능해진 상태.
철회 (Aborted)실패 상태에서 Rollback 명령에 의해 트랜잭션의 모든 변경사항이 이전 상태로 되돌려진 상태. 즉, 트랜잭션이 취소되고 데이터는 아무 변화도 없는 것처럼 유지됨.

1. Dirty Read (더티 리드)

  • 정의
    아직 커밋되지 않은(=확정 전) 데이터를
    다른 트랜잭션이 읽는 현상입니다.

  • 문제점
    만약 최초 트랜잭션이 롤백된다면,
    이미 읽어간 트랜잭션은 존재하지 않는 값을 사용하게 됩니다.

  • 예시

  • 기본 동작

  • Transaction 1: x에 y를 더한다.

  • Transaction 2: y를 70으로 바꾼다.
    - 동작 과정-

    1. Transaction 1이 x(10)를 읽고, y(70)를 읽어서 x+y=80으로 계산.
    2. 하지만 Transaction 2가 y=70 작업을 롤백(abort) 하면,
      실제 y는 20으로 돌아간다.
    3. 정상적으로라면 x=30이어야 하지만,
      이미 70을 사용해서 x=80이 되는 오류가 발생.

2. Non-Repeatable Read (비반복적 읽기)

  • 정의
    하나의 트랜잭션이 같은 데이터를 두 번 읽을 때,
    그 사이 다른 트랜잭션이 값을 수정하여
    두 번째 읽기에서 결과가 달라지는 현상입니다.

  • 문제점
    한 트랜잭션 내에서 읽은 값이 일관성 없이 달라질 수 있음.

  • 예시

    • 기본 동작
    • Transaction 1: x를 두 번 읽는다.
    • Transaction 2: x에 40을 더한다.
      - 동작 과정-
      1. Transaction 1이 x=10을 읽음.
      2. Transaction 2가 x=10을 읽고 40을 더해 x=50으로 바꾸고 commit.
      3. Transaction 1이 다시 x를 읽음 → x=50.
      4. 한 트랜잭션 내에서 x가 10 → 50으로 바뀌는 비반복적 읽기 현상 발생.

3. Phantom Read (팬텀 리드)

  • 정의
    한 트랜잭션이 같은 조건으로 여러 번 조회할 때,
    그 사이 다른 트랜잭션이 새로운 행을 삽입/삭제해서
    결과가 달라지는 현상입니다.

  • 문제점
    한 번은 없던 데이터가, 같은 조건에서 다음 조회에는 등장
    → 데이터의 정합성에 혼란이 생김

  • 예시

    • 기본 동작
    • Transaction 1: v=10인 데이터를 조회한다.
    • Transaction 2: t2의 v를 50에서 10으로 바꾸고 commit.
      - 동작 과정-
      1. Transaction 1이 v=10을 조회하면 t1만 조회됨.
      2. Transaction 2가 t2의 v를 10으로 바꾼 후 commit.
      3. Transaction 1이 다시 v=10을 조회하면 t1, t2 모두 결과로 나옴.
      4. 같은 조건, 다른 결과 → 팬텀(유령)처럼 행이 나타나는 현상

락(Lock) 메커니즘

락 메커니즘 이란?

  • 트랜재션 락 매커니즘이은 데이터베이스에서 여러 트랜잭션이 동시에 데이터에 접근할 때의 문제를 해결하기 위한 핵심 매커니즘 이다.

공유락과 배타락

  • 공유락(Shared Lock, S Lock)

    • 데이터를 읽기(read)만 할 때 거는 락입니다.
    • 여러 트랜잭션이 동시에 같은 데이터에 공유락을 걸 수 있어, 동시 읽기는 가능하지만,
    • 이 상태에서는 쓰기(수정) 작업은 불가능합니다.
    • 예: 여러 사람이 같은 책을 '읽을' 수는 있지만, 누군가 수정하려면 읽는 사람 모두가 책을 내려놔야 함.
  • 배타락(Exclusive Lock, X Lock)

    • 데이터를 쓰기(write, update, delete)할 때 거는 락입니다.
    • 오직 하나의 트랜잭션만 해당 데이터에 접근할 수 있습니다.
    • 이 락이 걸린 데이터는 읽기/쓰기 모두 차단되어, 다른 트랜잭션이 접근 불가합니다.
    • 예: 한 사람이 책에 '수정'하려면, 그 동안 아무도 읽거나 쓸 수 없음.

공유락(S Lock)과 배타락(X Lock) 사용 규칙 정리

1. 락이 없는 데이터라면?

  • 아무 트랜잭션도 락을 안 걸고 있다면
    → 누구든 자유롭게 공유락(S Lock) 또는 배타락(X Lock)을 걸 수 있음.

2. 트랜잭션이 데이터 X를 읽기만 할 때

  • 읽기만 할 거면
    공유락 S(X)를 요청함.
  • 읽거나 쓸 거면(수정/삭제)
    배타락 X(X)를 요청함.

3. 이미 공유락(S Lock)이 걸려 있는 경우

  • 누군가 S Lock을 이미 걸고 있다면?
    • 추가로 S Lock을 거는 건 허용됨.
      (즉, 여러 트랜잭션이 동시에 읽기 작업을 할 수 있음)
    • 하지만 X Lock(쓰기/수정 락)을 요청하면 거절됨.
      (읽는 사람 다 끝나야 쓸 수 있음)

4. 이미 배타락(X Lock)이 걸려 있는 경우

  • 누군가 X Lock을 걸고 있다면?
    • S Lock(읽기)도, X Lock(쓰기)도
      모두 거절됨
    • 오직 락을 건 트랜잭션 하나만 해당 데이터에 접근 가능

5. 락을 얻지 못한 트랜잭션은?

  • 락 요청이 거절되면?
    • 락이 풀릴 때까지 대기 상태가 됨(블로킹)

🔑 한 줄 요약

  • 공유락(S Lock): 여러 트랜잭션이 동시에 읽기만 가능
  • 배타락(X Lock): 오직 한 트랜잭션만 읽기/쓰기가 가능, 나머지는 모두 대기

예시 상황

데이터 락 상태신규 S Lock 요청신규 X Lock 요청설명
락 없음가능가능락이 없으니 누구나 락 가능
S Lock 한 명 있음가능불가읽기는 OK, 쓰기는 X
S Lock 여러 명 있음가능불가읽기는 OK, 쓰기는 X
X Lock 한 명 있음불가불가모두 대기!

트랜잭션 격리수준(Isolation Level)

트랜잭션의 격리 수준(Isolation Level)
여러 트랜잭션이 동시에 작업할 때 발생할 수 있는
Dirty Read, Unrepeatable Read, Phantom Read
문제에 대한 허용 여부를 정의합니다.

아래 표는 각 격리 수준별로
어떤 문제가 허용(○)되고,
어떤 문제는 방지(×)되는지 나타냅니다.

각 격리 수준에서 허용/방지되는 현상

  • O(○): 해당 현상이 발생할 수 있다 (막아주지 않는다)
  • X(×): 해당 현상이 발생하지 않는다 (막아준다)

1. Read Uncommitted

  • Dirty Read, Unrepeatable Read, Phantom Read 모두 발생 가능
  • 가장 낮은 수준의 격리(성능은 빠르지만, 신뢰성 매우 낮음)

2. Read Committed

  • Dirty Read는 방지, 하지만
  • Unrepeatable Read, Phantom Read는 여전히 발생 가능
  • 대부분의 RDBMS(오라클 등) 기본값

3. Repeatable Read

  • Dirty Read, Unrepeatable Read는 방지
  • Phantom Read는 발생 가능
  • MySQL InnoDB 기본값

4. Serializable

  • 모든 현상(Dirty/Unrepeatable/Phantom Read) 방지
  • 완벽한 일관성(단, 성능이 가장 느림, 동시성 제한 큼)

이걸 보면서 궁금해진게 생겼다. 왜 다막지 않고, 일부만 막는 단계(격리 수준)가 있는 걸까? 찾아보니 주요 내용은 이러했다.

1. 완벽하게 다 막으면(Serializable)

  • 모든 트랜잭션이 순차적으로 일어나는 것처럼 동작하기 때문에 데이터 충돌, 일관성 문제는 100% 방지할 수 있습니다.
  • 하지만, 동시에 여러 사용자가 접근하는 시스템에서는 속도가 너무 느려지고 대기 시간이 길어집니다.
  • 예를 들어, 대형 쇼핑몰에서 모든 결제·조회·업데이트를 무조건 '하나씩'만 처리한다면, 실시간 서비스가 어렵습니다.

2. 느슨하게 막으면(Read Committed, Repeatable Read 등)

  • 성능(동시성)이 좋아집니다. 여러 트랜잭션이 어느 정도 겹치면서 작업할 수 있기 때문입니다.
  • 그 대신, 데이터 일관성에 '약간의' 문제(예: Dirty Read, Phantom Read)가 생길 수 있지만, 대부분의 서비스에서는 '완벽'한 일관성보다는 빠른 처리와 실용적인 신뢰성이 더 중요합니다.

2단계 락킹(2PL: Two-Phase Locking) 프로토콜

트랜잭션의 락 관리 방식 중 하나로, 일관성(Serializability)을 보장하기 위해 사용합니다.
"락을 다 얻은 후에만 해제할 수 있다"라는 두 가지 단계로 운영됩니다.

  1. 성장 단계(Growing Phase)

    • 트랜잭션이 필요한 락을 계속 획득할 수 있지만, 해제는 할 수 없음
    • 필요한 락을 모두 얻을 때까지 이 단계
  2. 축소 단계(Shrinking Phase)

    • 한번이라도 락을 해제하면, 그 뒤로는 획득 불가
    • 이미 얻은 락만 해제 가능

2PL 프로토콜을 따르면 데드락이 발생할 수 있지만,
트랜잭션의 일관성(Serializability)은 항상 보장됨


트랜잭션 관리 시 주의사항

  • 무한루프
    트랜잭션이 정사적으로 종료되지 못하고 계속 실행되면, 시스템 리소스를 잡아먹고 다른 작업에 영향을 줌.

  • 락 장기 점유
    하나의 트랜잭션이 락을 오래 쥐고 있으면,
    다른 트랜잭션들이 대기하면서 전체 성능이 저하됨.
    심하면 데드락 위험도 커짐.

  • 적절한 롤백 처리
    오류나 예외 상황에서 Rollback을 해주지 않으면
    데이터가 이상한 상태로 남게 됨.


트랜잭션에서 발생 가능한 문제(데드락 등) 간단 소개

  • 데드락(Deadlock)
    두 개 이상의 트랜잭션이 서로 상대방의 락 해제를 기다리며
    무한정 대기에 빠지는 현상.

데드락의 원인, 탐지, 해결법 등은
별도 챕터([교착상태(Deadlock)])에서 다룰 예정입니다.


참고 자료

profile
개발에 대한 고민과 성장의 기록을 일기장처럼 성찰하며 남기는 공간

0개의 댓글