> For the complete documentation index, see [llms.txt](https://leehyokyun.gitbook.io/openbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://leehyokyun.gitbook.io/openbook/3-4.-db.md).

# 3-4. DB 동시 버전 관리(동시성)

데이터베이스의 동시 버전 관리, 동시성에 대하여

### 📌 데이터베이스 구조 및 동작 원리

### 1. DB 엔진별 기본 구조 및 계층 차이

데이터베이스 시스템은 인스턴스, 데이터베이스, 스키마, 테이블을 관리하는 물리적·논리적 계층 구조가 엔진마다 다르게 설계되어 있다.

* MySQL: 단일 인스턴스 아래에 데이터베이스(Schema와 동일한 개념으로 취급)가 존재하며, 그 하위에 테이블이 위치하는 구조를 지닌다.
* Oracle: 단일 인스턴스 아래에 단일 데이터베이스가 존재하며, 그 내부에 논리적 분리 단위인 다수의 스키마가 존재하고 각 스키마 하위에 테이블이 위치한다.
* PostgreSQL: 단일 인스턴스 아래에 다수의 독립적인 데이터베이스(기본적으로 계정명과 동일한 이름으로 생성)가 존재할 수 있으며, 각 데이터베이스 내부에 public을 기본으로 하는 다수의 스키마 계층이 존재하고 그 하위에 테이블이 구조화된다.

### 2. 트랜잭션 격리 수준(Isolation Level)과 동시성 문제점

트랜잭션 격리 수준은 내부적으로 트랜잭션이 Read View 일관성을 어떻게 유지하느냐에 대한 규칙이며, 이를 보장하지 못할 때 데이터 정합성이 깨지는 다양한 동시성 문제가 발생한다. 현대적인 DB에서 READ UNCOMMITTED는 정합성 문제도 크고, 성능 최적화 기법의 발전으로 상위 격리 수준과의 성능 차이가 0\~3%로 미미하여 실무에서 거의 선택되지 않는다.

* Dirty Read: 커밋하지 않은, 즉 트랜잭션 도중의 변경사항이 적용된 데이터를 다른 트랜잭션이 읽는 현상이다. 데이터를 읽어간 트랜잭션이 최종적으로 롤백(Rollback)되면, 내가 읽은 값은 존재하지 않는 유령 값이 되어 데이터 무결성이 심각하게 파괴된다. READ COMMITTED 격리 수준 이상부터는 이 Dirty Read가 완벽히 차단된다.
* Non-Repeatable Read: 한 트랜잭션 내에서 같은 행을 다시 읽었을 때 값이 다르게 나타나는 현상이다. 이는 내 트랜잭션이 진행되는 중간에 다른 트랜잭션이 해당 데이터를 UPDATE하고 커밋했기 때문에 발생한다. REPEATABLE READ 수준 이상에서 방지된다.
* Phantom Read: 한 트랜잭션 내에서 같은 조건으로 데이터를 다시 조회했을 때, 최초 조회 시에는 없던 레코드가 나타나거나 사라져서 결과 행의 개수가 달라지는 현상이다. 이는 트랜잭션 도중 다른 트랜잭션이 데이터를 INSERT하거나 DELETE하여 커밋하기 때문에 발생한다.
* Lost Update (갱신 분실): 두 개 이상의 트랜잭션이 동시에 동일한 Row를 수정할 때, 먼저 수정된 내역이 누락되고 나중 트랜잭션의 변경 사항만 덮어써 지는 실무적인 동시성 문제이다. InnoDB의 REPEATABLE READ는 일관된 스냅샷 읽기를 보장하지만, 이로 인해 다른 트랜잭션의 변경 내역을 추출하지 못해 오히려 Lost Update가 발생할 가능션을 내포한다. 이 문제는 SERIALIZABLE 수준으로 격리해야 완벽히 방지된다.

### 3. DB별 MVCC 버전 저장 및 가시성 판단 기준

동시성을 제어하고 일관된 읽기를 제공하기 위해 각 DB 엔진은 과거 버전을 저장하는 방식과 가시성(Visibility)을 판단하는 고유의 식별 기술을 채택하고 있다.

* Oracle (SCN 기반 Undo Segment):
  * 과거의 변경 데이터를 Undo 세그먼트(Undo Segment)에 저장한다.
  * 모든 커밋 시점에 시간 기준의 절대적 식별자인 SCN(System Chain Number)을 부여하여 데이터의 가시성을 판단하는 기준으로 삼는다.
  * Read Committed 격리 수준을 스냅샷 기반으로 관리하므로 Dirty Read는 완벽히 방지하지만 Phantom Read는 발생할 수 있다.
* PostgreSQL (Tuple 기반 Chaining 및 xmin/xmax):
  * 과거 데이터를 별도의 로그에 분리하지 않고 테이블 내에 Tuple 형태로 직접 보관하며, Chaining 기술을 통해 과거 기록을 연결하는 구조를 취한다.
  * 각 Tuple 내부에 삽입 트랜잭션 식별자인 xmin과 삭제/업데이트 트랜잭션 식별자인 xmax 컬럼을 보관하여 가시성을 판단한다.
  * Read Committed 격리 수준을 MVCC 기반으로 관리하며, Dirty Read는 불가능하나 Phantom Read는 발생 가능하다.
* MySQL InnoDB (trx\_id + Read View 및 Undo Log):
  * 실제 테이블에는 최신 버전의 로우(Tuple) 데이터만을 보관하고, 과거의 변경 이력은 Undo 로그(Undo Records) 영역에 기록한다.
  * 롤백 포인터와 트랜잭션 식별자인 trx\_id, 그리고 Read View 메커니즘을 조합하여 데이터의 가시성을 정교하게 판단한다.
  * 갭 락(Gap Lock)과 넥스트 키 락(Next-Key Lock)을 활용하여 Serializable에 가까운 동시성 제어 수준을 보유함으로써 REPEATABLE READ 격리 수준에서도 Phantom Read를 효과적으로 제어한다.
  * 애플리케이션 계층에서 versioning 컬럼이나 timestamp 컬럼을 사용하여 낙관적 락(Optimistic Lock)을 추가로 구현하여 진행할 수도 있다.

### 4. Read View와 Undo 로그 기반의 가시성 판단 (Visible)

MySQL InnoDB에서 특정 데이터 버전이 현재 트랜잭션 화면에 보일 수 있는지 여부를 결정하는 visible 조건은 격리 수준과 트랜잭션 활성 상태를 나타내는 Read View 및 Undo 로그의 체이닝 관계를 기반으로 확정된다.

* 가시성(Visible)의 본질: 특정 버전을 만든 트랜잭션 ID(trx\_id)가 현재 나의 Read View 기준에서 허용된 범위 안에 존재함을 의미한다. low\_limit\_trx\_id보다 작은 트랜잭션은 모두 visible하며, 활성 트랜잭션 목록(active trx ids)에 있거나 high\_limit보다 큰 미래의 트랜잭션은 invisible 처리된다.
* 버전 체이닝 탐색: 만약 현재 읽은 최신 레코드의 trx\_id가 가시성 조건에 부합하지 않아 내 트랜잭션 안에서 볼 수 없는 컨텍스트라면, 레코드에 포함된 롤백 포인터를 통해 Undo 로그를 역순으로 따라가며(Chaining) 내 Read View 기준에서 가시성이 허용되는 과거 버전의 컨텍스트를 찾아내어 가져온다.
* 잠금과의 관계: 트랜잭션이 특정 레코드에 락(Lock)을 걸어 다른 트랜잭션의 실시간 접근 및 수정을 막고 있는 상황이라 할지라도, 무조건 Undo 로그에는 이전 데이터 기록이 명확히 남으므로 다른 트랜잭션의 MVCC 읽기 작업은 차단되지 않는다.

### 5. DB 엔진별 MVCC 내부 동작 과정의 차이점

조회 연산이 실행될 때 스냅샷을 구성하고 데이터를 최종 컨텍스트화하는 메커니즘은 엔진별로 상이하게 동작한다.

* MySQL의 MVCC 조회 방식:
  * 일반적인 SELECT 조회(Consistent Read) 실행 시, 현재 시점의 트랜잭션 활성 상태를 바탕으로 일종의 스냅샷인 Read View를 추출한다. 격리 수준이 READ COMMITTED면 쿼리(Statement) 실행 시마다 Read View를 매번 생성하고, REPEATABLE READ면 트랜잭션의 첫 번째 조회 시에만 생성하여 이를 재사용한다.
  * 최신 버전의 로우를 먼저 읽은 뒤, 다른 트랜잭션이 남긴 Undo 로그 내역과 현재 Read View를 비교하는 가시성(visible) 조건 판단을 수행하여 허용 가능한 데이터 컨텍스트를 최종 스냅샷팅(Snapshotting)한다.
  * 반면, FOR UPDATE 또는 FOR SHARE 등 잠금을 수반하는 조회(Current Read/Locking Read)의 경우 MVCC 스냅샷을 사용하지 않고 반드시 데이터베이스 내의 실제 최신 버전 로우를 직접 추출하여 처리한다.
* PostgreSQL 및 Oracle의 MVCC 조회 방식:
  * 설정된 데이터베이스 격리 수준에 철저히 기반하여 MVCC 및 Snapshot 메커니즘을 구동한다.
  * 쿼리가 시작된 시점 또는 트랜잭션 시작 시점의 격리 수준 기준에 맞춰 스냅샷을 추출할 로우 후보군들을 전방위적으로 탐색한다.
  * MVCC 동작 없이 바로 최신 버전의 로우를 읽는 구동 방식이 존재하지 않으며, 무조건 MVCC 기반의 스냅샷 방식으로 읽기를 진행한다. FOR UPDATE 등의 락이 필요한 상황에서도 스냅샷 동작(Undo 세그먼트 또는 Tuple 체인)을 통해 최신 로우를 정교하게 탐색하여 컨텍스트화한다.

### 6. 데이터베이스 락 메커니즘 (Locking)

락 메커니즘(Locking)은 특정 레코드나 데이터 영역에 대한 동시적인 트랜잭션의 쓰기 연산과 이로 인해 유발되는 충돌을 물리적으로 제어하는 아키텍처적 장치이다.

* Consistent Read와 Lock의 무시: 일반적인 SELECT 조회(Consistent Read)는 MVCC 기반 스냅샷을 추출하여 구동되므로, 특정 레코드에 락이 걸려 있더라도 이를 우회하여 Undo 로그에서 이전 데이터를 안정적으로 읽어낼 수 한다. 즉, 다른 트랜잭션이 수정을 위해 락을 쥐고 있어도 읽기 작업은 대기하지 않는다.
* Current Read와 X-Lock 충돌: SELECT ... FOR UPDATE나 UPDATE/DELETE 연산과 같이 최신 데이터를 직접 확보해야 하는 Current Read의 경우, 최신 로우를 읽음과 동시에 배타적 잠금(Exclusive Lock, X-Lock) 획득을 시도한다. 이때 이미 다른 트랜잭션이 해당 데이터를 수정 중이거나 락을 점유하고 있다면 락 충돌이 발생한다.
* 충돌 발생 시 분기 처리: 락 충돌 상황에서 시스템의 아키텍처 및 쿼리 옵션에 따라 LOCK WAIT(락이 해제될 때까지 대기), LOCK SKIP(락이 걸린 로우는 건너뛰고 다음 로우 처리), 또는 NOT WAIT/FAIL(즉시 실패 처리) 등 정교한 분기 처리를 수행하여 동시성 제어 흐름을 제어한다.
* MySQL InnoDB의 Gap Lock과 Next-Key Lock 전략:
  * InnoDB는 단순히 조회된 개별 Row 자체만 잠그는 레코드 락(Row Lock)에 그치지 않고, 인덱스 레코드 사이의 빈 구간에 새로운 데이터가 삽입(INSERT)되는 것을 차단하는 갭 락(Gap Lock)을 지원한다.
  * 범위 조건 조회(예: BETWEEN 120 AND 260 FOR UPDATE) 시, 범위 안에 매칭되는 실제 데이터 행들 자체뿐만 아니라 그 데이터들의 앞뒤에 존재하는 빈 공간(Gap)들까지 통째로 잠그는 넥스트 키 락(Next-Key Lock = Row Lock + Gap Lock) 전략을 사용한다.
  * 이처럼 동등 조건이 아닌 범위 조건에 대해 빈 공간까지 조밀하게 잠그기 때문에, 트랜잭션 도중 다른 트랜잭션이 범위 내에 유령 레코드를 삽입하는 행위를 완벽히 방쇄하여 REPEATABLE READ 수준에서도 Phantom Read 현상을 원천 차단하는 강력한 동시성 제어 능력을 발휘한다.

### 7. Locking

#### 동작 과정, 수준, 행위 주체에 따른 분류

* 동시성은 어디서 발현된 개념인가? = 내부적으로 트랜잭션이 Read View 일관성을 유지하느냐, 이 Read View를 읽는 다양한 스냅샷, 그 방법이 MVCC인 것이다.
  * ReadView는 규칙을 구현한결과, 일관된 읽기를 특정 수준으로 보장하는 동작의 결과.

* 동시적으로 DB의 Row에 접근(SELECT) 및 수정(UPDATE)하는 시도에 대한 정책

  * 비관락/분산락

  DB 레벨이 아닌 Application Level에서의 충돌 허용, 충돌 이후의 재시도 및 추가 정책

  * 낙관락

  경쟁 스레드의 동작을 허용하느냐, 멈추느냐, 실행 중인 스레드의 자원 확보 시도를 지속하는가

  * 데드락/라이브락

### 📌 DB별 MVCC 동작 원리 : 각 DB의 "조회'는 어떻게 이루어지는가

### MySQL : Readview 및 Undo log 기반의 컨텍스트 추출

* 현재 row에 대해 먼저 Readview(\*활성트랜잭션 목록 및 min/max trx id, 현재 트랙잭션 id가 저장된 컨텍스트)를 생성하며, 그 이후 실제로 해당 row를 읽어 해당 row를 작업한 트랜잭션 id를 읽는다.
* 트랜잭션 ID의 단조성을 이용하여, 트랜잭션 이전에 생성된 row라면 visible하다고 판단하여 그대로 Readview에 현재 읽은 Row 컨텍스트를 추출한다. 반면에 트랜잭션 이후에 생성된 row일 경우 invisible하다고 판단, Undo log를 따라가면서 트랜잭션 id를 비교하여 visible한 컨텍스트를 판단 및 최종 추출한다.

### PostgreSQL : 스냅샷 먼저 추출 후, 해당 row의 Tuple Versioning

* 데이터 row를 저장하는 페이지 안에는 해당 row 데이터 및 작업한 트랜잭션의 내역이 Tuple 형태로 저장되어있다.
* 현재 트랜잭션이 생성한 스냅샷과 해당 row의 Tuple에 기록된 트랜잭션 Versioning을 비교하여(xmin/xmax),  해당 DB 격리수준에 기반한 visible 하다면 그대로 스냅샷으로 추출하며, invisible 하다면 Tuple Versioning을 통해 가능한 데이터를 추출하여 스냅샷에 반영한다.

### Oracle : Row block 스냅샷 추출 후, Undo Segment의 SCN Versioning

* Oracle의 경우 현재 row block 정보만 DB page에 저장하며, 그 이전에 생성된 row block 및 내역들은 Undo Segment라는 별도의 공간에 Undo Record로 저장한다.
* 조회 시 먼저 현재 row block에 대한 스냅샷 및 SCN을 추출하고, 해당 Row block의 SCN Versioning을 비교하여 visible하다면 그대로 스냅샷으로 추출한다. invisible 하다면 Undo Segment의 Undo Record의 SCN Versioning을 비교해가면서 visible한 컨텍스트를 추출, 반영한다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://leehyokyun.gitbook.io/openbook/3-4.-db.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
