-
트랜잭션은 데이터베이스에서 여러 작업을 하나의 원자적 단위로 실행하기 위한 구조로, 읽기·쓰기·갱신·삭제를 포함함
- 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은 동일한 목표를 서로 다른 내부 구조로 달성함
- 네 가지 격리 수준과 트랜잭션 동작 원리를 이해하면 데이터베이스를 보다 안정적으로 운용 가능함