Notes
DB 09. 트랜잭션과 회복
데이터베이스 트랜잭션의 ACID 특성, 동시성 제어, locking, 2PL, recovery, REDO/UNDO, WAL, checkpoint, Oracle/PL/SQL 트랜잭션 명령과 isolation level을 정리한다.
- Published
- Updated
- Area
- Databases
- Type
- concept
- Series
- Database Systems
- Category
- Notes
1. 핵심 관점
트랜잭션을 배우는 이유는 단순히 COMMIT과 ROLLBACK 문법을 외우기 위해서가 아니다. 실제 DBMS 환경에서는 여러 사용자가 동시에 같은 데이터베이스에 접근하고, 그 와중에 시스템 장애가 발생할 수 있다. 따라서 DBMS는 다음 두 가지를 보장해야 한다.
| 구분 | 핵심 목적 | 관련 주제 |
|---|---|---|
| Concurrency Control | 여러 트랜잭션이 동시에 실행되어도 결과가 올바르게 유지되도록 함 | serializability, locking, 2PL, isolation level |
| Recovery | 장애가 발생해도 트랜잭션의 결과를 올바르게 복구함 | log, REDO, UNDO, WAL, checkpoint |
트랜잭션은 결국 동시에 실행해도 안전하고, 장애가 나도 안전한 논리적 작업 단위를 의미한다.
2. Transaction의 정의
Transaction은 데이터베이스 응용에서 하나의 논리적인 작업 단위를 수행하는 데이터베이스 연산들의 모임이다. 하나의 트랜잭션은 여러 개의 SELECT, INSERT, UPDATE, DELETE로 구성될 수 있다.
예를 들어 계좌 이체는 SQL문 두 개로 표현될 수 있다.
UPDATE CUSTOMER
SET BALANCE = BALANCE - 100000
WHERE CUST_NAME = '정미림';
UPDATE CUSTOMER
SET BALANCE = BALANCE + 100000
WHERE CUST_NAME = '안명석';
하지만 업무적으로는 이 두 SQL문이 하나의 작업이다. 출금만 되고 입금이 되지 않으면 안 된다. 따라서 두 문장은 하나의 transaction으로 묶여야 한다.
계좌 이체 transaction
= 출금 UPDATE + 입금 UPDATE
= 둘 다 성공하거나, 둘 다 취소되어야 하는 논리적 단위
주의할 점은 DBMS가 SQL문의 업무 의미를 자동으로 이해하지는 못한다는 것이다. 여러 SQL문을 하나의 transaction으로 취급해야 한다면 사용자가 명시적으로 범위를 제어해야 한다.
3. Transaction이 필요한 대표 예시
3.1 전체 사원 급여 6% 인상
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
500명의 사원 급여를 인상하는 도중 320번째 사원까지 수정한 상태에서 시스템이 다운되었다고 하자. 이때 DBMS가 아무 조치를 하지 않으면 320명만 인상되고 나머지 180명은 그대로인 비일관 상태가 된다.
따라서 DBMS는 다음 둘 중 하나를 보장해야 한다.
- 500명 전원의 급여가 모두 수정된다.
- 한 명의 급여도 수정되지 않은 상태로 되돌린다.
이 문제는 transaction의 Atomicity와 recovery의 필요성을 보여준다.
3.2 계좌 이체
계좌 이체에서는 출금과 입금이 하나의 transaction이어야 한다.
| 단계 | SQL 의미 | 장애 발생 시 문제 |
|---|---|---|
| 1 | 정미림 잔액 -100000 | 출금만 반영될 수 있음 |
| 2 | 안명석 잔액 +100000 | 입금 전 장애 시 전체 잔액이 틀어짐 |
정상적인 DBMS는 두 SQL문이 모두 수행되거나, 하나도 수행되지 않은 상태로 복구해야 한다.
3.3 항공기 예약
항공기 예약 transaction은 보통 다음 흐름을 가진다.
1. 항공편의 SEAT_SOLD, CAPACITY 조회
2. 빈 좌석이 있으면 SEAT_SOLD 증가
3. RESERVED 테이블에 고객 예약 정보 INSERT
4. COMMIT
이 중 2번까지만 수행되고 3번 전에 장애가 발생하면, 팔린 좌석 수는 증가했지만 실제 예약 고객 정보가 없는 상태가 된다. 따라서 조회, 좌석 수 증가, 예약 정보 삽입은 하나의 transaction으로 묶여야 한다.
4. ACID 특성
Transaction의 핵심 성질은 ACID이다.
| 성질 | 의미 | 깨지는 예시 | 주로 관련된 DBMS 기능 |
|---|---|---|---|
| Atomicity | 모두 수행되거나 전혀 수행되지 않아야 함 | 계좌 이체에서 출금만 반영 | recovery, UNDO |
| Consistency | 수행 전후 DB가 일관된 상태여야 함 | 제약조건 위반, 총액 불일치 | integrity constraint, concurrency control, recovery |
| Isolation | 동시에 수행되어도 혼자 수행한 것처럼 보여야 함 | 마지막 1좌석을 두 명이 예약 | concurrency control, locking |
| Durability | commit된 결과는 장애 후에도 유지되어야 함 | commit 직후 장애로 결과 소실 | recovery, REDO, log |
4.1 Atomicity
Atomicity는 한 transaction 안의 연산들이 all or nothing으로 처리되어야 한다는 뜻이다.
성공: 모든 연산 반영
실패: 모든 연산 취소
계좌 이체에서 출금만 성공하고 입금이 실패하면 Atomicity가 깨진다.
4.2 Consistency
Consistency는 transaction 수행 전 DB가 일관된 상태였다면, 수행 후에도 일관된 상태여야 한다는 뜻이다.
단, transaction 수행 중간에는 일시적으로 불일치 상태가 생길 수 있다. 예를 들어 계좌 이체에서 출금 UPDATE 후 입금 UPDATE 전에는 총액이 일시적으로 맞지 않을 수 있다. 하지만 transaction이 끝난 뒤에는 다시 일관된 상태가 되어야 한다.
4.3 Isolation
Isolation은 여러 transaction이 동시에 수행되더라도, 결과는 어떤 순서로 하나씩 수행한 것과 같아야 한다는 뜻이다.
즉, 실제 실행은 섞여도 되지만 최종 결과는 serial schedule과 동등해야 한다.
4.4 Durability
Durability는 transaction이 COMMIT된 뒤에는 시스템 장애가 발생하더라도 결과가 사라지지 않아야 한다는 뜻이다.
commit된 transaction의 결과가 디스크에 완전히 기록되지 않은 상태에서 장애가 발생할 수 있으므로, DBMS는 log를 이용해 REDO를 수행한다.
5. COMMIT과 ROLLBACK
Transaction은 성공적으로 끝날 수도 있고, 실패하여 철회될 수도 있다.
| 명령 | 의미 | 결과 |
|---|---|---|
COMMIT | 현재 transaction의 변경 결과를 확정 | 변경 내용이 DB에 반영됨 |
ROLLBACK | 현재 transaction의 변경 결과를 취소 | transaction 시작 전 상태로 되돌림 |
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
COMMIT;
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
ROLLBACK;
COMMIT은 성공적인 종료이고, ROLLBACK은 비성공적인 종료이다.
6. Concurrency Control
6.1 목적
DBMS는 다수 사용자 환경에서 여러 transaction을 동시에 수행한다. 동시 실행은 성능을 높이지만, transaction 간 간섭으로 잘못된 결과를 만들 수 있다.
Concurrency control은 여러 transaction이 동시에 실행되어도 결과가 어떤 serial schedule과 동등하도록 보장하는 기법이다.
7. Schedule의 종류
7.1 Serial Schedule
Serial schedule은 여러 transaction을 한 번에 하나씩 차례대로 수행하는 schedule이다.
트랜잭션이 n개라면 가능한 serial schedule의 수는 다음과 같다.
n!
예를 들어 T1, T2, T3 세 개가 있다면 가능한 순서는 3! = 6개이다.
T1 → T2 → T3
T1 → T3 → T2
T2 → T1 → T3
T2 → T3 → T1
T3 → T1 → T2
T3 → T2 → T1
필기 포인트는 n = transaction의 개수이고, transaction 내부 연산은 섞이지 않는다는 점이다.
7.2 Non-serial Schedule
Non-serial schedule은 여러 transaction의 연산이 서로 섞여 수행되는 schedule이다.
트랜잭션 T1, T2, …, Tk가 각각 n1, n2, …, nk개의 연산을 가진다면, 내부 순서를 유지하면서 섞이는 경우의 수는 다음과 같이 볼 수 있다.
(n1 + n2 + ... + nk)! / (n1! n2! ... nk!)
여기서 nk는 k번째 transaction의 연산 횟수이다.
예를 들어 T1에 연산 2개, T2에 연산 2개가 있다면 가능한 interleaving 수는 다음과 같다.
(2 + 2)! / (2! 2!) = 6
7.3 Serializable
Serializable은 non-serial schedule이더라도 그 결과가 어떤 serial schedule의 결과와 동등한 경우를 말한다.
실제 실행: T1과 T2의 연산이 섞임
허용 조건: 최종 결과가 T1 → T2 또는 T2 → T1 중 하나와 같음
Concurrency control의 목표는 모든 transaction을 반드시 serial하게 실행하는 것이 아니라, 실제 실행은 non-serial이어도 결과는 serializable하게 만드는 것이다.
8. DBMS 내부 연산: Input, Output, read_item, write_item
DBMS 내부에서는 디스크, 버퍼, 프로그램 변수 사이에서 값이 이동한다.
| 연산 | 이동 방향 | 의미 |
|---|---|---|
Input(X) | disk → buffer | X가 포함된 block을 주기억 장치 buffer로 읽음 |
Output(X) | buffer → disk | X가 포함된 block을 disk에 기록 |
read_item(X) | buffer → program variable | buffer의 X 값을 프로그램 변수로 복사 |
write_item(X) | program variable → buffer | 프로그램 변수 값을 buffer의 X에 기록 |
주의할 점은 read_item(X)와 write_item(X)가 직접 disk를 읽고 쓰는 연산이 아니라는 것이다. 실제 disk I/O는 Input(X), Output(X)가 담당한다.
9. 동시성 제어가 없을 때 생기는 문제
| 문제 | 의미 | 핵심 원인 |
|---|---|---|
| Lost Update | 한 transaction의 갱신을 다른 transaction이 덮어씀 | 같은 값을 동시에 읽고 나중 write가 앞선 write를 무효화 |
| Dirty Read | commit되지 않은 데이터를 읽음 | 나중에 rollback될 수도 있는 값을 읽음 |
| Unrepeatable Read | 같은 데이터를 두 번 읽었는데 값이 달라짐 | 중간에 다른 transaction이 update 후 commit |
| Phantom Problem | 같은 조건으로 조회했는데 새로운 행이 나타남 | 중간에 다른 transaction이 조건 범위에 row insert |
9.1 Lost Update
초기값이 다음과 같다고 하자.
X = 300000
Y = 600000
T1은 X에서 Y로 100000을 이체하고, T2는 X에 50000을 더한다.
정상 결과는 다음과 같아야 한다.
X = 300000 - 100000 + 50000 = 250000
Y = 600000 + 100000 = 700000
하지만 두 transaction이 동시에 X = 300000을 읽으면 문제가 생긴다.
T1: read_item(X) -- X = 300000
T1: X = X - 100000 -- X = 200000
T2: read_item(X) -- X = 300000
T2: X = X + 50000 -- X = 350000
T1: write_item(X) -- X = 200000
T2: write_item(X) -- X = 350000, T1의 갱신 손실
최종적으로 T1의 출금 효과가 사라진다. 이것이 lost update이다.
9.2 Dirty Read
T1이 정미림의 잔액을 100000 감소시킨 뒤 아직 commit하지 않았다고 하자. 이때 T2가 평균 잔액을 읽고, 이후 T1이 rollback되면 T2는 실제로 확정되지 않은 값을 읽은 것이 된다.
T1: UPDATE balance = balance - 100000
T2: SELECT AVG(balance)
T1: ROLLBACK
Dirty read의 핵심은 commit되지 않은 임시 값을 읽었다는 것이다.
9.3 Unrepeatable Read
T2가 같은 query를 두 번 수행하는 사이에 T1이 값을 변경하고 commit하면, T2는 같은 데이터를 두 번 읽었는데 서로 다른 값을 보게 된다.
T2: SELECT AVG(balance) FROM account;
T1: UPDATE account SET balance = balance - 100000 WHERE cust_name = '정미림';
T1: COMMIT;
T2: SELECT AVG(balance) FROM account;
9.4 Phantom Problem
Phantom problem은 기존 행의 값이 바뀌는 문제가 아니라, 조건을 만족하는 새로운 행이 삽입되어 결과 집합 자체가 달라지는 문제이다.
SELECT col1
FROM A
WHERE col1 BETWEEN 1 AND 10;
첫 번째 결과가 1, 5였는데, 다른 transaction이 7을 insert한 뒤 같은 query를 다시 수행하면 1, 5, 7이 나온다. 새로 나타난 7이 phantom row이다.
10. Locking
Locking은 동시에 수행되는 transaction들의 간섭을 막기 위해 가장 널리 사용되는 concurrency control 기법이다.
| Lock | 이름 | 사용 목적 | 양립성 |
|---|---|---|---|
S-lock | Shared lock | 읽기 목적 | 다른 S-lock과 양립 가능 |
X-lock | Exclusive lock | 갱신 목적 | 다른 lock과 양립 불가능 |
Lock compatibility는 다음과 같이 정리할 수 있다.
| 현재 lock | 새 S-lock 요청 | 새 X-lock 요청 |
|---|---|---|
| 없음 | 허용 | 허용 |
S-lock | 허용 | 대기 |
X-lock | 대기 | 대기 |
핵심은 읽기-읽기만 동시에 가능하고, 쓰기가 개입되면 충돌한다는 것이다.
11. Two-Phase Locking Protocol
**2PL(two-phase locking protocol)**은 lock을 획득하는 단계와 해제하는 단계를 분리하여 serializability를 보장하는 protocol이다.
| 단계 | 이름 | 가능한 동작 | 불가능한 동작 |
|---|---|---|---|
| 1단계 | Growing phase | lock 획득 | unlock |
| 2단계 | Shrinking phase | unlock | 새로운 lock 획득 |
한 transaction이 필요한 모든 lock을 획득한 시점을 lock point라고 한다.
주의할 점은 lock을 하나라도 해제하면 shrinking phase에 들어간다는 것이다. 이후에는 새로운 lock을 요청할 수 없다.
11.1 Lock 해제 시점의 trade-off
필기에서 강조된 핵심은 다음과 같다.
| 방식 | 장점 | 단점 |
|---|---|---|
| lock을 조금씩 빨리 해제 | 동시성 증가 | 정확성 제어 약화 가능 |
| transaction 끝에서 한꺼번에 해제 | 정확성, isolation 강화 | 동시성 저하 |
즉, lock을 오래 잡을수록 안전하지만 다른 transaction은 더 오래 기다려야 한다.
고립 수준 ↑ → 정확성 ↑, 동시성 ↓
고립 수준 ↓ → 동시성 ↑, 정확성 ↓
이 trade-off는 이후 isolation level에서도 그대로 등장한다.
12. Deadlock
2PL은 serializability를 보장하는 데 유용하지만 deadlock이 발생할 수 있다.
T1: X에 X-lock 획득
T2: Y에 X-lock 획득
T1: Y에 X-lock 요청 → T2가 보유 중이므로 대기
T2: X에 X-lock 요청 → T1이 보유 중이므로 대기
이 상태에서는 T1과 T2가 서로를 기다리므로 아무도 진행할 수 없다.
Deadlock 처리 방식은 크게 두 가지이다.
- Prevention: deadlock이 발생하지 않도록 사전에 제한한다.
- Detection and recovery: deadlock을 탐지한 뒤, 일부 transaction을 abort하여 lock을 해제한다.
13. Multiple Granularity Locking
Lock의 단위는 여러 수준이 가능하다.
Database
└── Relation
└── Disk block / Page
└── Tuple / Record
| Lock 단위 | 장점 | 단점 |
|---|---|---|
| 큰 단위 | lock 관리 overhead 작음 | 동시성 낮음 |
| 작은 단위 | 동시성 높음 | lock 관리 overhead 큼 |
예를 들어 EMPLOYEE relation의 block b1 안에 t1, t2, t3, t4, t5가 있다고 하자.
T1이t1,t4를 updateT2가t2를 read
Tuple 단위 lock이라면 T1과 T2는 동시에 수행 가능하다. 하지만 T1이 block b1 전체에 X-lock을 걸면, T2는 t2만 읽으려 해도 기다려야 한다.
14. Recovery
Recovery의 목적은 장애 상황에서도 transaction의 Atomicity와 Durability를 보장하는 것이다.
| 장애 시점 | 문제 | 필요한 회복 동작 | 보장 성질 |
|---|---|---|---|
| commit 전 장애 | 일부 갱신이 DB에 반영되었을 수 있음 | UNDO | Atomicity |
| commit 후 장애 | 갱신 결과가 disk에 완전히 반영되지 않았을 수 있음 | REDO | Durability |
15. REDO와 UNDO
15.1 REDO
REDO는 commit된 transaction의 갱신을 다시 수행하는 것이다.
commit log 있음 → 완료된 transaction → REDO 대상
Transaction이 commit되었지만 변경 내용이 disk에 완전히 기록되기 전에 시스템이 다운될 수 있다. 이 경우 recovery module은 log를 보고 해당 transaction의 변경 내용을 다시 반영한다.
15.2 UNDO
UNDO는 commit되지 않은 transaction의 갱신을 취소하는 것이다.
start log는 있지만 commit log 없음 → 미완료 transaction → UNDO 대상
Transaction이 commit 전에 일부 데이터를 disk에 기록했을 수 있으므로, recovery module은 log의 old value를 사용해 원래 값으로 되돌린다.
16. Log 기반 Immediate Update
Immediate update에서는 transaction이 commit되기 전이라도 갱신 내용이 buffer를 거쳐 disk database에 기록될 수 있다.
이 방식에서는 commit되지 않은 transaction의 결과도 disk에 반영될 수 있으므로 log가 반드시 필요하다.
16.1 Log record 유형
| Log record | 의미 | Recovery에서의 역할 |
|---|---|---|
[Trans-ID, start] | transaction 시작 | transaction 추적 시작 |
[Trans-ID, X, old_value, new_value] | X를 old value에서 new value로 수정 | old_value는 UNDO, new_value는 REDO에 사용 |
[Trans-ID, commit] | transaction 성공 완료 | REDO 판단 기준 |
[Trans-ID, abort] | transaction 철회 | 취소 완료 기록 |
Update log record에 old_value와 new_value가 모두 들어가는 이유는 명확하다.
UNDO에는 old_value가 필요하다.
REDO에는 new_value가 필요하다.
16.2 Commit point
Commit point는 transaction의 모든 갱신 연산이 끝나고, 해당 갱신 사항이 log에 기록된 시점이다.
Recovery module은 log를 보고 다음과 같이 판단한다.
| Log 상태 | 판단 | 회복 동작 |
|---|---|---|
[T, start]와 [T, commit] 모두 있음 | 완료된 transaction | REDO |
[T, start]는 있지만 [T, commit] 없음 | 미완료 transaction | UNDO |
핵심 판단 기준은 commit log의 존재 여부이다.
17. WAL: Write-Ahead Logging
**WAL(Write-Ahead Logging)**은 database buffer를 disk에 쓰기 전에, 해당 변경을 설명하는 log record를 먼저 disk에 기록해야 한다는 원칙이다.
log 먼저 기록 → database page 기록
만약 database page가 먼저 disk에 기록되고 log가 기록되기 전에 시스템이 다운되면, disk에는 변경된 데이터가 남아 있지만 이를 되돌릴 old value가 log에 없을 수 있다. 그러면 UNDO가 불가능해진다.
따라서 recovery를 위해 다음 순서가 중요하다.
1. Log buffer를 disk에 force
2. Database buffer를 disk에 flush
3. 필요 시 checkpoint log record 기록
필기에서 → WAL이라고 표시된 부분은 바로 이 원칙을 강조한 것이다.
18. Checkpoint
Log를 처음부터 끝까지 모두 검사하면 recovery 시간이 길어진다. 이를 줄이기 위해 DBMS는 주기적으로 checkpoint를 수행한다.
Checkpoint는 특정 시점까지의 log와 database buffer 상태를 disk에 반영하여, recovery 시 검사 범위를 줄이는 기준점이다.
18.1 Checkpoint 수행 과정
일반적인 checkpoint 과정은 다음과 같다.
- 수행 중인 transaction을 일시 중지할 수 있다.
- log buffer를 disk에 강제로 기록한다.
- database buffer를 disk에 강제로 기록한다.
[checkpoint]log record를 기록한다.- checkpoint 시점에 수행 중이던 transaction ID를 함께 기록한다.
- 중지된 transaction을 재개한다.
여기서도 중요한 순서는 log buffer가 database buffer보다 먼저 disk에 기록되어야 한다는 점이다. 이것이 WAL과 연결된다.
18.2 Checkpoint의 효과
| 구분 | Recovery 대상 |
|---|---|
| checkpoint 없음 | 오래전에 commit된 transaction까지 log를 검사해야 함 |
| checkpoint 있음 | checkpoint 이전에 완료되고 disk 반영된 transaction은 무시 가능 |
Checkpoint는 recovery의 정확성을 위한 기능이면서 동시에 recovery 시간을 줄이기 위한 최적화이다.
19. Backup과 재해적 고장
Disk 자체가 손상되는 catastrophic failure는 log만으로 복구하기 어렵다. 이 경우에는 database backup이 필요하다.
| 고장 유형 | 예시 | 회복 방식 |
|---|---|---|
| Non-catastrophic failure | 시스템 다운, transaction 오류 | log 기반 REDO/UNDO |
| Catastrophic failure | disk 손상, database 접근 불가 | backup 기반 복원 |
전체 database와 log를 주기적으로 backup해야 하며, 변경분만 백업하는 incremental backup을 사용하면 운영 부담을 줄일 수 있다.
20. Oracle/PLSQL Transaction 제어
Oracle에서 transaction은 실행 가능한 첫 번째 SQL문이 실행될 때 시작된다. 이후 COMMIT, ROLLBACK, SAVEPOINT 등으로 명시적으로 제어할 수 있다.
20.1 COMMIT
COMMIT;
현재 transaction에서 수행한 DML 결과를 확정하고 transaction을 완료한다.
20.2 ROLLBACK
ROLLBACK;
현재 transaction에서 수행한 DML 결과를 모두 취소하고 transaction을 철회한다.
20.3 SAVEPOINT
SAVEPOINT A;
Transaction 내부에 중간 저장점을 만든다.
ROLLBACK TO SAVEPOINT A;
지정한 savepoint 이후의 변경만 되돌린다.
예시는 다음과 같다.
DELETE FROM EMPLOYEE
WHERE DEPT = '임시부서';
SAVEPOINT A;
INSERT INTO EMPLOYEE (...)
VALUES (...);
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
SAVEPOINT B;
INSERT INTO EMPLOYEE (...)
VALUES (...);
| 명령 | 취소 범위 |
|---|---|
ROLLBACK TO SAVEPOINT B | B 이후 작업만 취소 |
ROLLBACK TO SAVEPOINT A | A 이후 작업 취소 |
ROLLBACK | transaction 시작 이후 전체 취소 |
21. READ ONLY와 READ WRITE
21.1 READ ONLY
SET TRANSACTION READ ONLY;
SELECT AVG(SALARY)
FROM EMPLOYEE
WHERE DEPT = '개발부';
READ ONLY transaction은 데이터를 읽기만 한다. 갱신 작업을 하지 않는다고 DBMS에 알려주므로 concurrency를 높이는 데 도움이 된다.
다음과 같은 갱신 작업은 허용되지 않는다.
SET TRANSACTION READ ONLY;
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
21.2 READ WRITE
SET TRANSACTION READ WRITE;
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
READ WRITE transaction은 SELECT, INSERT, DELETE, UPDATE를 모두 수행할 수 있다.
22. Isolation Level
Isolation level은 한 transaction이 다른 transaction과 얼마나 강하게 고립되어야 하는지를 나타낸다.
핵심 trade-off는 다음과 같다.
| Isolation level | 정확성 | 동시성 |
|---|---|---|
| 낮음 | 낮음 | 높음 |
| 높음 | 높음 | 낮음 |
고립 수준 ↑ → 정확성 ↑, 동시성 ↓
고립 수준 ↓ → 동시성 ↑, 정확성 ↓
실제 Oracle에서 지원하는 isolation level은 DBMS 버전과 문법에 따라 확인 필요하다. 일반 SQL 표준의 isolation level과 특정 DBMS의 실제 구현은 다를 수 있다.
22.1 READ UNCOMMITTED
SET TRANSACTION READ WRITE
ISOLATION LEVEL READ UNCOMMITTED;
가장 낮은 isolation level이다. Commit되지 않은 데이터를 읽을 수 있으므로 dirty read가 발생할 수 있다.
22.2 READ COMMITTED
SET TRANSACTION READ WRITE
ISOLATION LEVEL READ COMMITTED;
Commit된 데이터만 읽는다. Dirty read는 막을 수 있지만, 같은 데이터를 다시 읽을 때 값이 달라지는 unrepeatable read는 발생할 수 있다.
22.3 REPEATABLE READ
SET TRANSACTION READ WRITE
ISOLATION LEVEL REPEATABLE READ;
한 transaction 안에서 이미 읽은 데이터는 다시 읽어도 같은 값을 보장한다. Dirty read와 unrepeatable read는 막을 수 있다.
하지만 필기에서 강조된 것처럼 phantom problem은 여전히 발생할 수 있다.
REPEATABLE READ → phantom problem 가능
이유는 기존 row의 값 변경은 막더라도, 조건 범위에 새 row가 insert되는 것까지 완전히 막지 못할 수 있기 때문이다.
22.4 SERIALIZABLE
SET TRANSACTION READ WRITE
ISOLATION LEVEL SERIALIZABLE;
가장 높은 isolation level이다. 결과가 serial schedule과 동등하게 보장되어야 하며, dirty read, unrepeatable read, phantom problem을 모두 막는다.
23. Isolation Level별 문제 발생 여부
| Isolation Level | Dirty Read | Unrepeatable Read | Phantom Problem |
|---|---|---|---|
READ UNCOMMITTED | 가능 | 가능 | 가능 |
READ COMMITTED | 불가능 | 가능 | 가능 |
REPEATABLE READ | 불가능 | 불가능 | 가능 |
SERIALIZABLE | 불가능 | 불가능 | 불가능 |
외우는 기준은 다음과 같다.
READ UNCOMMITTED: 다 가능
READ COMMITTED: dirty read만 방지
REPEATABLE READ: dirty read, unrepeatable read 방지
SERIALIZABLE: dirty read, unrepeatable read, phantom problem 모두 방지
정확성 순서는 다음과 같다.
READ UNCOMMITTED
< READ COMMITTED
< REPEATABLE READ
< SERIALIZABLE
동시성 순서는 반대이다.
SERIALIZABLE
< REPEATABLE READ
< READ COMMITTED
< READ UNCOMMITTED
24. 자칫 실수하기 쉬운 구분 기준
24.1 Dirty Read vs Unrepeatable Read
| 구분 | Dirty Read | Unrepeatable Read |
|---|---|---|
| 읽은 값의 상태 | commit되지 않은 값 | commit된 값 |
| 문제 원인 | 다른 transaction이 rollback될 수 있음 | 중간에 다른 transaction이 commit함 |
| 예시 | rollback될 값을 읽음 | 같은 SELECT를 두 번 했는데 값이 달라짐 |
24.2 Unrepeatable Read vs Phantom Problem
| 구분 | Unrepeatable Read | Phantom Problem |
|---|---|---|
| 달라지는 대상 | 기존 row의 값 | 결과 row 집합 |
| 원인 | 기존 row update/delete | 조건 범위에 row insert |
| 예시 | 같은 계좌 잔액이 달라짐 | 같은 조건 조회에서 새 사원이 나타남 |
24.3 UNDO vs REDO
| 구분 | UNDO | REDO |
|---|---|---|
| 대상 | commit되지 않은 transaction | commit된 transaction |
| 사용 값 | old_value | new_value |
| 목적 | Atomicity 보장 | Durability 보장 |
24.4 Lock 단위와 성능
| Lock 단위 | 동시성 | Overhead | 주의할 점 |
|---|---|---|---|
| Tuple 단위 | 높음 | 큼 | lock 관리 비용 증가 |
| Block/Page 단위 | 중간 | 중간 | 같은 block의 다른 tuple 접근도 막을 수 있음 |
| Relation 단위 | 낮음 | 작음 | 불필요한 대기 증가 가능 |
25. 전체 요약
9장 transaction의 흐름은 다음과 같이 정리할 수 있다.
9.1 Transaction 개요
- transaction은 하나의 논리적 작업 단위
- ACID: Atomicity, Consistency, Isolation, Durability
- COMMIT은 확정, ROLLBACK은 취소
9.2 Concurrency Control
- 동시에 실행해도 serial schedule과 동등한 결과를 보장해야 함
- Lost update, dirty read, unrepeatable read, phantom problem 주의
- Locking과 2PL로 serializability를 보장
- lock을 오래 잡으면 정확성은 높지만 동시성은 낮아짐
9.3 Recovery
- 장애 발생 시 commit 여부에 따라 REDO/UNDO 수행
- commit log 있음 → REDO
- commit log 없음 → UNDO
- WAL은 database page보다 log를 먼저 disk에 쓰는 원칙
- checkpoint는 recovery 범위를 줄이는 기준점
9.4 Oracle/PLSQL Transaction
- COMMIT, ROLLBACK, SAVEPOINT로 transaction 제어
- READ ONLY는 조회 전용, READ WRITE는 갱신 가능
- isolation level이 높을수록 정확성은 높고 동시성은 낮음
- REPEATABLE READ는 phantom problem이 남을 수 있음
- SERIALIZABLE은 가장 강한 isolation level
핵심은 transaction을 단순한 SQL 실행 단위가 아니라 정확성, 동시성, 장애 복구를 함께 만족해야 하는 DBMS의 논리적 실행 단위로 보는 것이다.