트랜잭션 경계에 관하여

Lim Jeong Hu·2025년 9월 18일
post-thumbnail

0. 트랜잭션 + 경계

데이터베이스도 을 긋는 게 중요하다. 어디서부터 트랜잭션이 시작되고, 어디서 끝나는 지 알아야 한다. 그래서

트랜잭션이란

  • 데이터베이스에서 여러 SQL 문장하나의 묶음(단위)으로 취급하는 것이다.

트랜잭션 경계란

  • 트랜잭션이 시작되고 끝나는 구간을 의미한다.
  • BEGIN/START → 여러 SQL 실행 → 성공 시 COMMIT, 실패 시 ROLLBACK으로 마무리한다.

세부 개념

  • Auto-commit: 일부 DB/클라이언트는 기본으로 각 문장을 자동 커밋한다. 다중 작업을 묶으려면 명시적 시작이 필요하다.
  • DDL 자동 커밋: 많은 DB에서 스키마 변경(DDL)은 트랜잭션을 암묵 커밋 또는 종료시킬 수 있다.
  • SAVEPOINT: 트랜잭션 내부의 부분 롤백 지점을 의미한다.

1. ACID란

DB가 약속하는 기본 네 가지의 원칙이 있는데, 이름은 간단하지만 실제로는 많은 내용을 담고 있다.

원자성 (Atomicity)

  • 모두 아니면 아무 것도 아니다.” 부분 성공은 금지.
  • Undo/Redo 로그, WAL 기반 복구를 통해 보장한다.
  • 깨지면 부분적 반영이 발생한다.
  • 체크리스트
    • 실패 지점에서 롤백 경로가 설계되어 있는가?
    • 외부 시스템에 동시에 쓰면 보상 트랜잭션 전략이 있는가?

일관성 (Consistency)

  • 트랜잭션 전후로 비즈니스 불변식과 DB 제약이 항상 만족되어야 한다.
  • PK/FK/UNIQUE, CHECK, 트리거, 애플리케이션 레벨 불변식으로 보장할 수 있다.
  • 일관성은 DB만의 책임이 아니며 애플리케이션도 함께 보장해야 한다.
  • 체크리스트
    • 핵심 불변식문서화 및 테스트했는가?
    • DB 제약으로 끌어내릴 수 있는가?

격리성 (Isolation)

  • 동시에 실행돼도 서로 간섭 없이 순차 실행처럼 보이게 한다.
  • 이상현상은 다음과 같다.
    • Dirty Read
      -> 다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽어버리는 현상
    • Non-repeatable Read
      -> 같은 조건으로 두번 조회했는데 결과가 다르게 나오는 현상
    • Phantom Read
      -> 같은 조건으로 조회했는데 새 레코드가 끼어드는 현상
    • Lost Update, Write Skew
      -> 둘 이상의 트랜잭션이 동시에 갱신하면서 서로의 변경이 덮여 사라지는 현상
  • 체크리스트
    • 필요한 격리 수준을 업무 단위별로 정의했는가?
    • 읽기 일관성이 크면 MVCC 이해가 필요한 것이다.

지속성 (Durability)

  • 커밋된 결과는 장애 후에도 영구적으로 보존된다.
  • WAL, fsync/그룹 커밋, 복제 및 백업을 통해 보장된다.
  • 설정에 따라 강도가 달라질 수 있다.
  • 체크리스트
    • 장애 또는 재시작 시 복구 시나리오가 검증되었는가?
    • 백업 및 복구 목표(RPO/RTO)가 수립되었는가?

2. 격리 수준 (Isolation Level)

동시에 여러 요청이 몰리면 꼬일 수 있다. 그래서 다음과 같은 격리 수준이 필수이다.

수준Dirty ReadNon-repeatablePhantom비고/기본값 예시
READ UNCOMMITTED가능가능가능일부 DB는 미지원(PostgreSQL)
READ COMMITTED불가가능가능기본값: Oracle, PostgreSQL
REPEATABLE READ불가불가(표준상) 가능MySQL InnoDB 기본. InnoDB는 Next-Key Lock으로 팬텀도 상당수 방지
SERIALIZABLE불가불가불가가장 안전(성능 비용 큼). 일부 DB는 충돌 시 에러로 종료

3. 동시성 제어 기법

누가 먼저 쓸지, 어떻게 충돌을 막을 지 정하는 방법으로 동시성 제어 기법을 사용하는데, 그 예시로는 다음과 같다.

비관적 잠금 (Pessimistic Locking)

  • 공유/배타 잠금, 의도 잠금, 갭락 및 넥스트키락을 사용한다.
  • 충돌이 잦을 때 안전하다.
  • 교착상태, 대기 및 타임아웃, 처리량 저하가 발생할 수 있다.
  • 업데이트 충돌이 빈번하고 비용이 큰 경우 사용된다.

낙관적 잠금 (Optimistic Locking)

  • 버전 또는 타임스탬프 필드, CAS 검증 후 재시도를 사용한다.
  • 동시성이 높고 잠금 점유가 적다.
  • 충돌 시 재시도 비용Starvation이 발생할 수 있다.
  • 충돌이 드문 읽기 환경에서 사용된다.

MVCC (Multi-Version Concurrency Control)

  • 쓰기새 버전을 생성하고 읽기시점 일관성을 확보한다.
  • Readers don’t block writers 특성을 가진다(DB별 차이가 있다).
  • 읽기 성능이 높아진다.
  • 장수 트랜잭션은 블로트 문제(불필요한 데이터의 공간 낭비)를 발생시킬 수 있다.

4) 교착상태 (Deadlock)

동시성 프로그램이다보니, 교착상태가 발생하기도 한다. 그래서

발생 원인

  • 두 트랜잭션이 서로가 가진 리소스를 기다리며 순환 대기를 형성할 때 발생한다.

예방 방법

  • 일관된 잠금 순서를 준수해야 한다.
  • 짧은 트랜잭션을 유지하고 배치를 쪼개야 한다.
  • 적절한 인덱스로 불필요한 범위 잠금을 줄여야 한다.
  • 타임아웃재시도 정책을 수립하고 재시도 시 idempotency를 고려해야 한다.

5. 저장점 (Savepoint)

전체 롤백이 아니라 중간 지점까지 되돌리고 싶은 경우는 SAVEPOINT(저장점) 을 사용하는데, 그래서

SAVEPOINT란

  • 트랜잭션 내부 체크포인트로 부분 롤백이 가능하다.

활용 예시

  • 대량 처리 중 일부 레코드만 취소하고 나머지를 진행할 수 있다.

6. 분산 트랜잭션

DB가 하나만 있으면 좋은데, 여러 시스템이 얽혀 있기 때문에 분산 트랜잭션이 등장하였다.

2PC (Two-Phase Commit)

  • 코디네이터가 참여자에게 PrepareCommit을 합의하는 방식이다.
  • 원자성을 확보할 수 있다.
  • 복잡성과 지연이 증가하고 장애 시 블로킹이 발생한다.

대안 패턴

  • Saga: 로컬 트랜잭션보상 트랜잭션으로 처리하는 방식이다.
  • Outbox/Inbox: 이벤트를 기록하여 안전하게 비동기 전파하는 방식이다.
  • Idempotency Key: 재시도 시 중복 효과를 방지하는 방식이다.
profile
종강주세요

0개의 댓글