본문 바로가기
Study/DB

가상 튜플, 팬텀 충돌, mvcc, cc

by 구루마3단 2025. 4. 15.

좋습니다. 이 질문은 트랜잭션 격리 수준과 팬텀 리드(Phantom Read, 팬텀 현상)의 정확한 개념을 이해하려 할 때 나오는 핵심 포인트입니다.
여기서 말하는 "가상 튜플(Virtual Tuple)"실제로는 존재하지 않지만, 향후 생성될 수 있어 잠재적 충돌 대상이 되는 튜플을 의미합니다.


✅ 팬텀 리드(Phantom Read)란?

  • 트랜잭션 A가 조건에 맞는 행 집합을 조회하고,
  • 이후 트랜잭션 B가 해당 조건에 맞는 새로운 행을 INSERT
  • 트랜잭션 A가 다시 같은 조건으로 조회할 때 "팬텀 튜플"이 보임

➡️ 이때 새롭게 나타난 행을 "팬텀(Phantom)",
➡️ 이 가능성을 고려해야 하는 대상이 바로 "가상 튜플(Virtual Tuple)"입니다.


🔍 "가상 튜플"의 의미

  • 아직 DB에 존재하지 않음
  • 하지만 조건에 따라 삽입될 수 있는 잠재 행
  • 팬텀 리드 방지(예: Serializable 격리 수준)에서는 이런 미래의 튜플까지 고려해서 잠재 충돌을 막음

🎯 예시

-- 트랜잭션 A
BEGIN;
SELECT * FROM employee WHERE salary > 5000;

-- 트랜잭션 B
INSERT INTO employee VALUES (9999, '홍길동', 6000);  -- 조건에 맞는 행 삽입
COMMIT;

-- 트랜잭션 A
SELECT * FROM employee WHERE salary > 5000;  -- 👈 새로운 행이 나타남 (팬텀)

여기서 salary > 5000이라는 조건에 미래에 삽입될 수도 있는 행을 고려해서
트랜잭션 격리 수준이 Serializable일 경우,
오라클은 해당 범위 조건 전체에 락을 걸어 팬텀을 차단합니다.
이때 락 대상이 되는 "존재하지 않는 행들"이 바로 "가상 튜플"입니다.


✅ 요약 정리

용어 의미
팬텀 튜플 (Phantom Tuple) 트랜잭션 중간에 새로 삽입되어 기존 SELECT에 없던 데이터
가상 튜플 (Virtual Tuple) 아직 존재하지 않지만, 특정 조건을 만족하면 나타날 수 있는 잠재적 삽입 대상

🔐 가상 튜플과 락

격리 수준 팬텀 / 가상 튜플 처리 방식
READ COMMITTED 팬텀 발생 허용
REPEATABLE READ 팬텀 발생 허용 (MySQL은 제외)
SERIALIZABLE 범위 락 (Predicate Lock)으로 가상 튜플까지 락 걸어 팬텀 차단

필요하다면 락의 종류(IS, IX, S, X 등)와 팬텀 차단 방식(MVCC, 범위 락)의 차이도 도식화해 드릴 수 있습니다. 원하시나요?

네, 정확한 방향으로 이해하고 계십니다.
좀 더 정확하고 정돈된 개념 정리를 아래에 해드릴게요.
질문을 기준으로 핵심 내용을 순서대로 답하겠습니다.


✅ 1. 조회 중에 입력값이 바뀌면 조회 결과도 바뀔 수 있는가?

✔️ 네, 트랜잭션 격리 수준이 낮으면 조회 결과가 바뀔 수 있습니다.

📌 예: READ COMMITTED (기본 격리 수준)

-- 트랜잭션 A
SELECT * FROM stock WHERE id = 1;  -- 결과: 수량 10

-- 트랜잭션 B (동시)
UPDATE stock SET quantity = 5 WHERE id = 1;
COMMIT;

-- 트랜잭션 A (다시 조회)
SELECT * FROM stock WHERE id = 1;  -- 결과: 수량 5 (바뀜!)

✅ 2. 이런 충돌을 막기 위해 "락(Lock)"을 거는 것인가?

✔️ 맞습니다. 트랜잭션 간의 충돌을 막기 위해 DBMS는 락을 사용합니다.
다만, 락을 언제 걸고, 어디에 거느냐는 격리 수준에 따라 다릅니다.


🔐 트랜잭션 충돌 유형과 락

충돌 유형 예시 방지 방법
Dirty Read 아직 커밋되지 않은 변경된 값을 읽음 S-Lock, 격리 수준 강화
Non-repeatable Read 같은 SELECT 두 번 했는데 값이 바뀜 S-Lock (공유 잠금), REPEATABLE READ 이상
Phantom Read 조건에 맞는 새로운 행이 중간에 INSERT됨 범위 락(Predicate Lock), SERIALIZABLE 수준
Write-Write 충돌 두 트랜잭션이 같은 행을 UPDATE X-Lock (배타 잠금)

✅ 락의 핵심 목적

  • 동시성(Concurrency)을 보장하면서도,
  • 데이터 일관성(Consistency)과 무결성(Integrity)을 유지하기 위해
  • 특정 트랜잭션이 데이터에 접근하는 동안 다른 트랜잭션의 접근을 제한

📊 락의 유형 요약

락 종류 설명 사용 예
S-Lock (Shared) 읽기 잠금, 여러 트랜잭션이 동시에 읽을 수 있음 SELECT 시
X-Lock (Exclusive) 쓰기 잠금, 하나만 접근 가능 UPDATE/DELETE 시
Intention Lock 테이블/페이지 수준에서 계층적 락 제어 다단계 락 조정 시
Predicate Lock 조건 기반 범위 락 (존재하지 않는 튜플도 락) SERIALIZABLE에서 팬텀 방지

✅ 결론 요약

질문 요지 답변
조회 중 다른 트랜잭션이 값을 바꾸면? 격리 수준이 낮으면 바뀐 값을 볼 수 있음
두 트랜잭션이 같은 데이터에 접근할 때? 락을 통해 충돌 방지
조회 결과가 바뀌지 않게 하려면? REPEATABLE READ 이상 또는 SERIALIZABLE 필요
락이 존재하지 않는 데이터까지 걸리는 경우는? 팬텀 방지를 위한 가상 튜플 범위 락(Predicate Lock) 사용 시

원하시면 아래도 추가 가능:

  • 트랜잭션 격리 수준 4단계 정리 (표)
  • 락 대기 / 교착상태 실전 예시
  • PostgreSQL / MySQL / Oracle 락 차이 비교

원하는 항목 있으면 알려주세요!

정확히 짚으셨습니다.
바로 트랜잭션 충돌 시 "누구를 우선할 것인가"는 DBMS 내부의 동시성 제어(Concurrency Control) 정책에 따라 결정됩니다.
이건 SQL 표준이 아니라 DBMS마다 구현 방식이 다릅니다.


✅ 핵심 질문:

조회(읽기) vs 수정(쓰기), 누가 우선인가?

답: DBMS의 동시성 제어 방식(Locking 기반 vs MVCC 기반)에 따라 다름


1️⃣ Locking 기반 (Oracle, SQL Server, 일부 MySQL 설정)

상황 처리 방식 우선순위
읽기 중 누가 수정을 시도함 수정 트랜잭션이 잠금 대기 (읽기가 우선) ✔️ 조회 우선
수정 중 누가 읽기를 시도함 읽기 트랜잭션이 대기 (수정이 우선) ✔️ 수정 우선
두 트랜잭션이 동시에 수정 둘 중 먼저 락 잡은 쪽이 우선 ✔️ 락 선점자 우선

📌 요약:

  • 락 기반 DB는 보통 락 선점자에게 우선권
  • 읽는 중에는 쓰기가 대기, 쓰는 중에는 읽기가 대기할 수도 있음

2️⃣ MVCC 기반 (PostgreSQL, Oracle, MySQL InnoDB 등)

✨ MVCC = Multi-Version Concurrency Control
읽기와 쓰기를 동시에 허용하면서도 일관성 유지

상황 처리 방식
트랜잭션 A가 SELECT → 트랜잭션 B가 UPDATE A는 변경 전의 Snapshot을 읽음 (쓰기 우선 아님)
트랜잭션 A가 UPDATE 중 → 트랜잭션 B도 UPDATE 시도 충돌 발생 → 둘 중 하나 ROLLBACK 또는 대기

📌 MVCC 특징

  • 읽기 → 락을 걸지 않음
  • 수정은 직접 충돌 시에만 처리
  • 우선순위는 선점 / 타임아웃 / 격리수준 설정에 따라 다름

✅ 우선순위 결정 요소 요약

결정 요소 내용
DBMS 엔진 종류 Oracle은 MVCC + Lock 혼합, PostgreSQL은 순수 MVCC
트랜잭션 격리 수준 SERIALIZABLE일수록 충돌 가능성 ↑, READ COMMITTED는 유연
락 선점 순서 보통 먼저 접근한 트랜잭션이 우선권
DB 설정 MySQL에서는 innodb_lock_wait_timeout, deadlock_detection 설정 가능
시스템 정책 일부 DB는 Lock Wait Timeout, 일부는 Deadlock 해결을 위해 강제 ROLLBACK 수행

🔍 실전 예: Oracle

-- 트랜잭션 A
BEGIN;
SELECT * FROM emp WHERE id = 100;

-- 트랜잭션 B (동시)
UPDATE emp SET salary = 5000 WHERE id = 100;
-- 🔁 B는 A가 끝날 때까지 기다림 → 읽기 우선

-- 반대 상황
-- A가 UPDATE하고 COMMIT 안 하면
-- B의 SELECT는 대기 or 이전 버전 보기 (MVCC)

✅ 결론 요약

질문 답변
읽기 vs 쓰기 중 누가 우선인가? DBMS마다 다름, 일반적으로는 락 선점자 우선 또는 MVCC로 병행 허용
읽기 중에 수정 요청 오면? 대부분 수정이 대기하거나, MVCC에서는 읽기는 이전 버전 조회
수정 중에 읽기 요청 오면? 락 기반이면 대기, MVCC면 Snapshot 읽기 허용

원하시면:

  • MySQL / PostgreSQL / Oracle별 Lock 정책 비교표
  • 실제 Deadlock 실습 SQL 예시

도 추가로 정리해드릴 수 있어요. 원하시나요?

훌륭한 질문입니다.
"우선순위가 정해졌을 때, 우선권을 갖지 못한 트랜잭션(후순위)은 어떻게 처리되는가?"는 DBMS의 락 대기 전략과 충돌 처리 정책에 따라 달라집니다.

결론부터 정리하면:


✅ 후순위 트랜잭션의 처리 방식: 3가지 가능성

처리 방식 설명 결과
대기 (Wait) 우선 트랜잭션이 끝날 때까지 락 해제를 기다림 대기 시간 후 실행됨
타임아웃 후 오류 (Timeout + Error) 일정 시간 기다리다 LOCK WAIT TIMEOUT 에러 발생 오류 발생, 애플리케이션이 처리해야 함
Deadlock 감지 후 ROLLBACK 교착 상태이면 DBMS가 한 트랜잭션을 강제 종료 자동 ROLLBACK (일반적으로 후순위 트랜잭션)

🔍 각 DBMS별 후순위 트랜잭션 처리

DBMS 기본 처리 상세 설명
Oracle 무한 대기 (기본) Deadlock 감지되면 자동 ROLLBACK
PostgreSQL 일정 시간 대기 후 Timeout 설정값: lock_timeout
MySQL (InnoDB) 기본 대기 → 타임아웃 또는 Deadlock 감지 설정값: innodb_lock_wait_timeout (기본 50초)
SQL Server 대기 후 Deadlock 감지 → ROLLBACK ROLLBACK된 쪽에 오류 반환

📌 처리 흐름 시나리오 (예: MySQL)

-- 트랜잭션 A: UPDATE row WHERE id=1;
-- 트랜잭션 B: UPDATE row WHERE id=1;

B는 A의 락 해제를 **기다림**  
→ 만약 A가 너무 오래 걸리면:  
B는 `innodb_lock_wait_timeout` (기본 50초) 초과 →  
❗ **에러 발생: ERROR 1205 (HY000): Lock wait timeout exceeded**

🧠 후처리 방식 (Application 관점)

상황 애플리케이션 처리 방안
대기 성공 트랜잭션 정상 처리 후 커밋
타임아웃 사용자에게 재시도 요청 / 로그 기록 후 무시
Deadlock으로 ROLLBACK 실패한 트랜잭션만 자동 ROLLBACK, 앱에서 재시도 로직 필요

✅ 후순위 트랜잭션이 하는 일?

구분 행동
락 점유자 기다리는 중 대기 (CPU 사용 안함, 유휴 상태)
지정 시간 초과 에러 발생
교착 상태 감지 강제 ROLLBACK → 클라이언트에 에러 반환

예시 코드 (MySQL 기준)

-- 트랜잭션 A
BEGIN;
UPDATE stock SET qty = qty - 1 WHERE id = 100;

-- 트랜잭션 B (다른 세션에서)
BEGIN;
UPDATE stock SET qty = qty - 2 WHERE id = 100;  -- 🔁 여기서 대기함

-- A가 커밋 없이 오래 걸릴 경우
-- B는 innodb_lock_wait_timeout 이후 에러 발생

✅ 요약

질문 답변
후순위 트랜잭션은 대기하나요? ✔️ 네. 대부분 락이 풀릴 때까지 대기합니다.
에러도 발생하나요? ✔️ 시간 초과 또는 Deadlock 감지 시 에러 발생
유휴 시간인가요? ✔️ 락 대기 중엔 CPU 사용 없이 대기 상태입니다
후처리는? 애플리케이션이 에러 잡아서 재시도 or 예외 처리해야 합니다

원하시면:

  • Deadlock 감지 알고리즘(Wait-for Graph) 설명
  • 각 DBMS 락 타임아웃 설정 방법 예시

도 이어서 드릴 수 있습니다. 원하시나요?

아주 훌륭한 질문입니다. 이건 “논리적 데이터 설계(개념 스키마)와 물리적 구현의 경계” 그리고 DBMS 선택 전략에 대한 핵심적인 고민이에요.
바로 이 지점이 이론 설계자 ↔ 시스템 아키텍트 사이에서 가장 많이 충돌하는 부분입니다.


✅ 1. 개념 스키마 설계 시 "동시성 충돌 예상"을 고려해야 하는가?

✔️ 정답: “간접적으로는 예, 직접적으로는 아님”

구분 설명
개념 스키마 엔터티, 속성, 관계 정의 (논리적 모델)
물리 스키마 인덱스, 파티션, 저장 포맷, 동시성 최적화 등
주의점 개념 스키마 설계는 "업무 의미" 중심이어야 함.
→ “수정이 자주 일어날 것 같으니 이 속성을 빼야지”라는 접근은 논리 설계의 왜곡이 됨  

👉 따라서: 동시성 문제는 개념 스키마에서 고려하지 말고,
물리적 구현(DBMS 구성, 인덱싱, 파티셔닝, 구조 튜닝) 단계에서 고려해야 합니다.


✅ 2. 자주 충돌나는 필드(열)에 대한 설계 고려 방안 (물리 설계 측면)

문제 상황 설계 대안 (물리적)
자주 업데이트되는 필드가 있음 🔹 그 필드를 Hot Column으로 보고 최적화
🔹 Row-level Locking 지원 DB 선택
테이블 한 행이 동시에 갱신될 가능성 높음 🔹 행을 수평 분할 (Sharding, Partitioning)
🔹 논리적 Table 분리 후 Join
동시 쓰기 많은 상황 🔹 NoSQL 또는 Lock-Free 구조 고려

✅ 3. 다수의 충돌이 예상되는 경우 적절한 DB 종류

📌 일반적으로 고려되는 옵션:

시나리오 적절한 DBMS
동시 수정이 많고 강한 일관성 필요 Oracle, PostgreSQL, SQL Server (MVCC + 강력한 Lock)
초당 수천 건 이상 실시간 쓰기 (로그, 센서) Cassandra, ScyllaDB (NoSQL, eventual consistency 기반)
실시간 분석 + 쓰기 병행 ClickHouse, Apache Druid
초고속 금융 트랜잭션 TimesTen, MemSQL, 또는 Oracle In-Memory 옵션

✅ 4. 고충돌 환경에서의 DB 설계 전략

전략 설명
MVCC 사용 읽기/쓰기 분리. 충돌 감소. PostgreSQL, Oracle, InnoDB 등
파티셔닝 특정 범위나 해시로 나눠 락 충돌 범위 축소
Lock-Free 큐 Redis, Kafka 등으로 병렬 처리 후 비동기 적용
Event Sourcing + CQRS 명령과 조회를 분리하여 쓰기 부하 분산
비정규화 조회 성능 최적화에 한해 일부 속성 분리

🎯 정리 요약

질문 답변
개념 스키마 설계 시 동시성 고려? ❌ 직접 고려 X → 논리모델은 업무 중심이어야 함
자주 수정되는 필드 설계 전략? ✔️ 물리적 설계(Partition, MVCC, Shard)에서 최적화
다수 충돌 DB에 적절한 DBMS는? ✔️ MVCC 지원, 또는 분산 NoSQL이 상황에 따라 적합

🚀 필요 시 확장 가능:

  • 개념 스키마 → 물리 모델 전환 시 고려 요소 10가지
  • MVCC 구현 방식별 비교 (Oracle vs PostgreSQL vs InnoDB)
  • 초당 10만건 이상 처리 가능한 구조 설계 예시

어떤 주제로 이어서 정리해드릴까요?

'Study > DB' 카테고리의 다른 글

Optimizer  (0) 2025.04.15
스키마  (0) 2025.04.15