데이터베이스 트랜잭션이란 무엇인가?

4 hours ago 3

  • 트랜잭션은 데이터베이스에서 여러 작업을 하나의 원자적 단위로 실행하기 위한 구조로, 읽기·쓰기·갱신·삭제를 포함함
  • MySQL과 Postgres는 begin;과 commit;으로 트랜잭션을 제어하며, 실패나 오류 시 rollback;으로 변경을 취소함
  • 두 데이터베이스 모두 일관된 읽기(consistent read) 를 보장하지만, Postgres는 다중 버전 행 저장(MVCC) 을, MySQL은 undo log를 사용함
  • 격리 수준(isolation level) 은 트랜잭션 간 데이터 간섭을 제어하며, Serializable부터 Read Uncommitted까지 네 단계로 구분됨
  • Postgres와 MySQL은 동시 쓰기 충돌을 서로 다른 방식으로 처리하며, Postgres는 낙관적 검증을, MySQL은 행 단위 잠금(row-level locking) 을 사용함

트랜잭션의 기본 개념

  • 트랜잭션은 데이터베이스에서 여러 SQL 작업을 하나의 원자적 실행 단위로 묶는 구조
    • begin;으로 시작해 commit;으로 종료하며, 중간에 여러 쿼리를 실행 가능
    • commit; 시점에 모든 변경이 한 번에 적용됨
  • 예기치 못한 장애(전원 차단, 디스크 오류 등)나 의도적 취소 시 rollback;으로 변경을 되돌림
    • Postgres는 WAL(Write-Ahead Log) 로 복구를 지원함
  • 트랜잭션 중 변경된 데이터는 격리되어 다른 세션에서 보이지 않음
    • rollback; 시 모든 변경이 취소되어 데이터베이스는 원래 상태로 복원됨

일관된 읽기(Consistent Reads)

  • 트랜잭션은 실행 중 외부 변경의 영향을 받지 않는 일관된 데이터 뷰를 유지해야 함
  • MySQL과 Postgres는 REPEATABLE READ 모드 이상에서 이를 지원하지만, 구현 방식이 다름
    • Postgres: 다중 버전 행 저장(MVCC) 으로 각 행의 버전을 관리
    • MySQL: undo log를 사용해 과거 버전을 재구성

Postgres의 다중 버전 행 저장

  • 행이 갱신될 때마다 새로운 버전이 생성되고, 이전 버전은 xmax, 새 버전은 xmin으로 트랜잭션 ID를 기록
  • 트랜잭션이 커밋되기 전에는 다른 세션이 변경 내용을 볼 수 없음
  • 커밋 후에는 새 버전이 전체 데이터베이스에 반영됨
  • rollback; 시 변경이 폐기되어 원래 데이터 유지
  • 오래된 행 버전은 VACUUM FULL 명령으로 정리되어 저장 공간을 회수함

MySQL의 Undo Log

  • MySQL은 행을 직접 덮어쓰지만, undo log에 이전 값을 기록해 필요 시 복원 가능
  • 각 행은 xid(최근 수정 트랜잭션 ID)와 ptr(undo log 포인터)을 메타데이터로 가짐
  • 동시에 여러 트랜잭션이 실행될 때, undo log를 통해 각 트랜잭션이 필요한 버전을 선택적으로 조회함
  • 동일 행에 여러 undo log 기록이 존재할 수 있으며, 트랜잭션 ID를 기준으로 적절한 버전을 선택함

격리 수준(Isolation Levels)

  • 트랜잭션 간 데이터 간섭을 제어하는 설정으로, Serializable → Repeatable Read → Read Committed → Read Uncommitted 순으로 완화됨
  • Serializable: 모든 트랜잭션이 순차적으로 실행된 것처럼 동작
  • Repeatable Read: 동일 쿼리 재실행 시 결과가 동일하지만, phantom read 가능
  • Read Committed: 이미 커밋된 다른 트랜잭션의 변경을 읽을 수 있음
  • Read Uncommitted: dirty read 허용, 가장 낮은 보호 수준이지만 성능은 높음

동시 쓰기(Concurrent Writes)

  • 두 트랜잭션이 동일 행을 동시에 수정할 때의 처리 방식은 데이터베이스별로 다름

MySQL: 행 단위 잠금(Row-level Locking)

  • 공유 잠금(S lock) 은 여러 트랜잭션이 동시에 읽기 가능
  • 배타 잠금(X lock) 은 한 트랜잭션만 행을 수정 가능
  • SERIALIZABLE 모드에서는 모든 갱신 시 X lock을 획득해야 하며, 충돌 시 교착 상태(deadlock) 발생 가능
  • MySQL은 교착 상태를 감지해 한쪽 트랜잭션을 종료시킴

Postgres: Serializable Snapshot Isolation

  • Postgres는 predicate lock을 사용해 행 집합 단위로 접근을 추적
    • 예: WHERE id BETWEEN 10 AND 20 조건에 대한 잠금
  • 실제 접근을 차단하지 않고, 충돌을 감지해 위반 시 트랜잭션을 종료함
  • 낙관적 충돌 해결(optimistic conflict resolution) 로 교착 상태를 피함
  • MySQL과 마찬가지로, 충돌 시 한 트랜잭션이 종료되며 애플리케이션은 재시도 로직을 구현해야 함

결론

  • 트랜잭션은 데이터베이스의 핵심 구성 요소로, 원자성·일관성·격리성·지속성(ACID) 을 보장함
  • Postgres와 MySQL은 동일한 목표를 서로 다른 내부 구조로 달성함
  • 네 가지 격리 수준과 트랜잭션 동작 원리를 이해하면 데이터베이스를 보다 안정적으로 운용 가능함

Read Entire Article