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

> Clean Markdown view of GeekNews topic #26930. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=26930](https://news.hada.io/topic?id=26930)
- GeekNews Markdown: [https://news.hada.io/topic/26930.md](https://news.hada.io/topic/26930.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-02-24T04:33:04+09:00
- Updated: 2026-02-24T04:33:04+09:00
- Original source: [planetscale.com](https://planetscale.com/blog/database-transactions)
- Points: 2
- Comments: 1

## Topic Body

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

## Comments



### Comment 51737

- Author: neo
- Created: 2026-02-24T04:33:04+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47110473) 
- 이 글이 좀 부족하게 느껴졌음  
  SQL 표준에서 정의한 **현상(phenomena)** 중심으로 격리 수준을 설명하는 대신, **직렬 가능성(serializability)** 개념에서 출발하는 게 더 직관적이라 생각함  
  직렬 가능성은 스레드 안전성의 일반화로 볼 수 있고, 이를 잃으면 실행 순서에 따라 결과가 달라지는 버그가 생김  
  데이터베이스의 다양한 격리 수준은 이 보장을 완화한 형태일 뿐이며, 사용자가 다른 방식으로 보장을 확보해야 함  
  현상들은 비직렬적 상황을 시각화하는 도구일 뿐, 직렬 가능성과 직접적으로 연결된 것은 아님  
  예를 들어 **Kubernetes 클러스터**도 잘 설계된 컨트롤러를 사용하면 직렬 가능하게 동작할 수 있음
  - 글쓴이임. 좋은 피드백 고마움  
    트랜잭션, 격리 수준, MVCC를 여러 DB 간 비교까지 포함해 한 번에 다루는 건 방대한 작업임  
    기술적 깊이와 접근성, 그리고 글의 길이 사이에서 균형을 잡으려 했음
  - [Jepsen: MariaDB Galera Cluster 분석](https://aphyr.com/posts/327-jepsen-mariadb-galera-cluster) 링크를 공유함  
    더 많은 **표기와 인용**이 있으면 좋겠다는 의견임
  - 대부분의 RDBMS는 필요하면 직렬 가능 격리를 제공함  
    하지만 불필요하게 사용하면 트랜잭션 간 **조정 비용**이 커져 동시성과 처리량이 줄어듦
  - 그렇다면 더 나은 설명을 제안해보라는 반응임

- 트랜잭션을 **copy-on-write 파일시스템(btrfs, zfs)** 의 스냅샷처럼 생각할 수도 있지만, **Git 브랜치**로 비유하는 게 더 직관적이라 봄  
  BEGIN은 브랜치 생성, UPDATE는 커밋, ROLLBACK은 브랜치 삭제, COMMIT은 `git merge`와 같음  
  충돌이 나면 DB가 행 단위로 병합을 시도하고, 실패 시 설정에 따라 롤백하거나 강제 병합함  
  READ UNCOMMITTED는 빠른 병합을, SERIALIZABLE은 정확성을 우선함  
  이런 비유가 누군가에게 트랜잭션의 개념을 ‘아하!’ 하고 이해하게 도와줄 수 있음
  - (짧은 댓글) 동시성(concurrency)을 암시하는 반응임

- 많은 사람들이 놀라는 부분은 **Postgres와 MySQL이 기본적으로 직렬 가능 모드가 아니라 read-committed**라는 점임  
  성능 차이는 “약간”이 아니라 실제로는 훨씬 큼  
  read-committed를 쓰면 **락(lock)** 관리에 신경 써야 하고, UNIQUE 제약도 경쟁 조건을 막는 데 필요함  
  그래도 직렬 가능 모드의 성능 손실과 재시도 문제를 감수하느니 이 방식을 선호함  
  참고: [PostgreSQL 공식 문서](https://www.postgresql.org/docs/current/transaction-iso.html)
  - 최신 MySQL과 MariaDB(InnoDB)는 기본이 **repeatable-read**임  
    [MySQL 문서](https://dev.mysql.com/doc/refman/8.4/en/set-transaction.html#set-transaction-isolation-level), [MariaDB 문서](https://mariadb.com/docs/server/reference/sql-statements/administrative-sql-statements/set-commands/set-transaction#isolation-level) 참고  
    MyISAM은 이제 거의 안 씀
  - SERIALIZABLE의 문제는 성능뿐 아니라 **충돌·데드락·타임아웃**으로 트랜잭션이 실패할 수 있다는 점임  
    애플리케이션이 이를 감지하고 재시도 전략을 가져야 함
  - Oracle과 SQL Server도 기본은 read committed임  
    직렬 가능 모드는 교과서에서는 멋져 보이지만 실제로는 거의 안 씀

- 요즘 많은 데이터베이스 툴이 **ACID보다 실시간 업데이트 공유**를 우선함  
  예를 들어 Airtable은 필드 수정 시 동료 화면에 바로 반영되지만, 트랜잭션이 없어 **데이터 불일치 위험**이 있음  
  관련 내용은 [VisualDB 블로그 글](https://visualdb.com/blog/concurrencycontrol/) 참고
  - 경쟁사 비판을 가장한 **제품 홍보**처럼 보인다는 반응임

- PlanetScale 블로그를 읽는 게 정말 즐거움  
  시각화에 어떤 도구를 썼는지 궁금함
  - 글쓴이임. 고마움!  
    시각화는 **js + gsap**([https://gsap.com](https://gsap.com))으로 제작했음

- 이 주제에 관심 있다면 **『Designing Data-Intensive Applications』** 을 강력히 추천함  
  다양한 격리 수준뿐 아니라 **ACID 정의의 모호성**까지 다룸  
  2판이 곧 나온다고 들음  
  - 2판이 이미 출간됨: [O’Reilly 링크](https://www.oreilly.com/library/view/designing-data-intensive-applications/9781098119058/)

- Postgres 같은 **MVCC 시스템**의 트랜잭션은 copy-on-write 파일시스템의 스냅샷과 비슷함  
  BEGIN 시점에 데이터 스냅샷을 만들고, UPDATE는 개인 복사본에만 반영됨  
  ROLLBACK 시 복사본은 폐기되고, COMMIT 시 새 스냅샷이 공식 버전이 됨  
  이 비유가 누군가에게 트랜잭션 개념을 명확히 이해시키는 계기가 될 수 있음  
  P.S. Git 브랜치 비유도 가능함  
  - 완전히 정확하진 않음. DB는 **브랜칭과 락킹을 함께 사용**함  
    SELECT 후 UPDATE 같은 경우 한 스레드가 블록될 수 있음  
    오늘 MySQL에서 이를 단일 쿼리로 바꿀 수 있을지 실험해볼 예정임

- 예전엔 백엔드 면접에서 트랜잭션을 자주 물었음  
  모두가 써봤지만 이해 수준은 경력에 따라 다름  
  격리 수준을 전부 외우진 않아도, 서로 다르게 동작한다는 걸 아는 것만으로도 **호기심과 시스템 이해도**를 볼 수 있음
  - 같은 이름의 격리 수준이라도 DB마다 동작이 다르므로, **케이스별로 세부 동작을 확인**해야 함

- “phantom read” 설명이 오해를 줄 수 있음  
  repeatable read에서는 기존 행의 값은 변하지 않지만 **새로운 행이 추가**될 수 있음  
  기존 행이 바뀌거나 삭제되는 일은 없으므로, 그 점을 명확히 해야 함

- “xmin/xmax와는 관련 없다”는 문장이 불완전하게 느껴짐  
  커밋 시 시각화가 테이블 헤더를 가리키는 것도 이상함  
  실제로는 **xmax/xmin이 커밋 여부를 판단하는 핵심 메커니즘** 아닌가?  
  서브트랜잭션까지 고려하면 더 복잡해짐  
  그래도 시각화와 설명은 전반적으로 즐겁게 읽었음
  - 나도 **xmax/xmin 개념**이 빠진 게 아쉽다고 느낌  
    격리 수준을 이해하는 데 핵심인데, 마치 섹션이 누락된 것처럼 느껴졌음
