프로젝트/데이터베이스

디비 아키텍처 - Undo

쿠키담임선생님 2025. 8. 19. 12:42

undo 는 롤백용임.


redo 와 마찬가지로 빠른 읽기/쓰기 를 위해 버퍼 캐시에 캐싱.

데이터 유실 방지를 위해 변경사항을 redo 로그에 로깅.

각 트랜젝션 별로 undo 세그먼트를 할당 (한 세그먼트를 두개의 트랜젝션이 공유가능)

총 3가지 목적을 위해 사용된다.

  1. transaction rollback
    1. 롤백 시 사용
  2. transaction recovery
    1. 장애 상황 발생 시 redo 를 통해 최종 커밋되지 않은 변경사항까지 복구된다.
  3. read consistency
    1. 다른 sql 들은 lock을 통해 읽기 일관성 구현
    2. 오라클은 undo 데이터를 이용해 읽기 일관성 구현

redo 는 로그에 저장되는데 undo는 세그먼트에 저장된다고?


뭐가 다른데..?

🛠️ 쉽게 비유하면

  • Undo 세그먼트: 바뀌기 전 상태의 데이터를 보관하는 비밀 금고 (트랜잭션마다 열고 닫음).
  • Redo 로그 파일: 바뀐 내용을 기록하는 블랙박스 (시스템 전체 차원의 연속된 기록).

세그먼트란?


세그먼트와 데이터 블록의 관계

  • 데이터베이스는 가장 작은 저장 단위를 **데이터 블록(Block)**으로 관리하고,
  • 여러 블록이 모여 **익스텐트(Extent)**를 이루며,
  • 익스텐트가 모여 하나의 **세그먼트(Segment)**를 형성함.

Undo 세그먼트의 구성은 그러면 위에서 말한 여러 익스텐트가 모여서 이루어짐.

가장앞의 익스텐트는 세그먼트의 헤더 정보가 담긴다.

세그먼트 헤더에는 또 트랜잭션 테이블 슬롯이 위치.

여기에 기록되는것은 4개

  1. 트랜잭션 아이디
  2. 트랜잭션 상태정보
  3. 커밋 SCN
  4. Last UBA (undo block adress)

위 정보는 딱히 중요.

중요한 것은 트렌젝션의 종류당 undo 레코드 갯수가 다르다.

insert, delete는 하나

update는 내부적으로 딜리트 후 인서트 해서 두개.

그리고 중요한게

아직 커밋되지 않은 active상태의 슬롯은 재사용 안됨.

하지만 커밋된 후의 committed 상태의 슬롯은 재사용 된다.

하지만 바로 재사용되는것은 아니고 가장 먼저 커밋된 슬롯부터 순차적으로.

따라서 커밋 이후 데이터가 상당기간 남아있음.

문장수준 읽기 일관성


undo 의 역할중 하나.

필요성 : sql 문이 실행되다가 다른 트랜잭션에 의해 데이터가 변경된다면 일관성이 없다.

이런 현상을 방지하기 위해 문장수준 읽기 일관성을 유지.

보통 해당 방법은

트랜잭션 격리 수준을 올리거나

테이블 락 사용.

오라클은 락을 사용하지 않고 undo 세그먼트에 저장해둔 데이터를 활용.

그렇다고 너무 고립 레벨을 올리면 동시성이 급격히 떨어지게 되고 이는 성능 저하임.

하지만 오라클은 높은 수준의 동시성을 유지하면서도 완벽한 읽기 일관성을 보장한다.

어떻게?


순서는 아래와같음

1. 오라클에서는 읽기 일관성을 지원함.

👉 맞아. 오라클은 쿼리가 실행되는 동안 다른 트랜잭션의 변경에 영향을 받지 않도록 읽기 일관성을 제공해.

2. consistent 모드를 사용할 때 일관성을 유지하는 방법은 cr 블록을 생성하는 것임.

👉 맞아. 일관성을 유지하기 위해 Undo 정보를 이용해 Consistent Read 블록을 생성하는 방식으로 일관된 결과를 보장해.

3. 블록을 읽는 시점에 변경되었는지 확인해서 변경된 블록이면 cr 블록을 생성하는 것임.

👉 맞아. 쿼리 시작 SCN과 블록의 변경 시점을 비교해, 필요하면 Undo를 통해 CR 블록을 만들어서 보여줘.

4. 3번에 대한 변경된 이력은 블록 헤더 정보에 있는 ITL 슬롯의 SCN 상태정보를 확인하는 것임. (이때 undo 세그먼트를 활용)

👉 맞아. ITL 슬롯에 각 트랜잭션의 변경 내역(트랜잭션 SCN, Undo 위치 등)이 기록돼 있어서, 그 정보를 바탕으로 Undo를 활용해 CR 블록을 만들지 결정해.

5. 여기에는 언제 트랜잭션이 커밋되었는지가 기록된다.

👉 맞아. ITL 슬롯에는 해당 트랜잭션이 커밋됐는지 여부와 커밋 SCN 정보가 저장된다.

6. 이를 통해 현재 데이터를 조회하는 과정에서 변경된 블록이 있는지 확인하고, 있다면 cr 블록을 포함해서 조회하게 된다.

👉 맞아. 쿼리 실행 중 변경된 블록이 있다면 Undo를 이용해 CR 블록을 만들어 일관성을 보장한 후 결과를 반환해.

하나하나 내가 정리해서 지피티한테 맞냐고 물어본 결과 정확하다고 함.

나는 정확히 이해했다.

여기서 나오는 개념이 undo 세그먼트. 그리고 해당 세그먼트의 헤더에 들어있는 ITL 슬롯정보인데, 이부분은 완전히 정확이 이해안해도된다.

그냥 구조만 파악하면된다.

ITL 슬롯정보에는 커밋 시점정보가 들어있다 이정도만 알아도 될듯.

이제 current 모드 읽기에 대해 알아보자.

왜 current 모드가 필요하지?

그냥 다 consistent 를 사용해야 데이터 일관성이 보장되잖아?

왜 current read가 꼭 필요할까? - 무결성을 보장하기 위해.

  • 데이터 변경 작업은 무조건 최신 상태에서 락을 걸고 수정해야 데이터 무결성이 보장돼.
  • 과거 시점으로 되돌린 CR 블록은 읽기 전용 가상 데이터이기 때문에, CR 블록을 수정할 수는 없어.
  • 즉,
    • CR 모드 → 일관성 있는 조회
    • Current 모드 → 최신 블록을 기준으로 수정 및 락 걸기

결국 업데이트나 딜리트는 최신거가 필요함.

그럼 이게 consistent 와 다른점은?

consistent는 쿼리가 실행된 시점의 데이터.

current는 데이터를 찾아간 그 시점에 대한 데이터.

오라클에서는 consistent로 데이터를 읽은 이후 current로 데이터를 갱신한다고 하는데 처음부터 current로 읽고 current로 갱신하면 안되는건가?


읽기조차 current를 사용하면 안되는 이유

✅ 예제 상황 가정

테이블 accounts(account_id, balance)에 다음 데이터가 있다고 가정할게:

account_id balance

1 1000

📌 트랜잭션 A

트랜잭션 A가 먼저 다음 쿼리를 수행함:

sql
복사편집
-- 트랜잭션 A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;

  • 트랜잭션 A는 아직 COMMIT하지 않고 작업 중.
  • 따라서 account_id=1의 balance는 DB 블록 상 최신 값으로 900으로 변경됐지만, 트랜잭션 A만 볼 수 있는 상태야.
  • 다른 트랜잭션에는 아직 1000으로 보여야 일관성이 유지돼.

📌 트랜잭션 B (읽기)

이 시점에 트랜잭션 B가 동시에 accounts를 조회한다고 가정하자.

1️⃣ 올바른 동작(consistent read)이라면 트랜잭션 B는 쿼리 시작 시점 기준으로 balance=1000을 읽음.

2️⃣ 하지만 만약 트랜잭션 B가 current read로 최신 블록을 읽도록 한다면?

  • 트랜잭션 B는 트랜잭션 A가 아직 커밋하지 않은 변경을 그대로 읽게 됨 → Dirty Read 발생!
  • balance=900이라는, 아직 확정되지 않은 데이터를 보게 되어 잘못된 의사결정(예: 중복 결제 승인)으로 이어질 수 있음.

📌 문제점 (current read로 조회했을 때)

  • 아직 확정되지 않은 다른 트랜잭션의 변경사항(커밋 전 데이터)을 읽음 → Dirty Read.
  • 트랜잭션 A가 나중에 ROLLBACK한다면? 트랜잭션 B는 존재하지도 않게 될 900을 기준으로 잘못된 처리를 이미 끝냈을 수 있음.
  • 결과적으로 데이터 무결성이 깨지고, 비즈니스 로직 상 심각한 문제가 발생할 수 있음.

🔎 Dirty Read 예시 상황 시뮬레이션

  • 트랜잭션 A: balance 1000 → 900으로 변경 중, 아직 미커밋.
  • 트랜잭션 B(읽기): current read → balance=900으로 잘못된 정보 읽음.
  • 트랜잭션 A: 롤백 → balance는 다시 1000으로 복구됨.
  • 트랜잭션 B는 존재하지 않는 balance=900으로 작업을 진행한 셈이 돼서 데이터 일관성 완전 붕괴.