테마
13화 · 파고들기
필요할 때 찾아보는 문서다. 처음 읽을 때는
1_사건.md만으로 충분하다. 이 문서의 숫자는 모두 실측값이다.ora-hanorder(12화 종료 상태)의 복사본(_C13)에서 쟀고, 실습 스크립트 번호를 함께 적었다. redo 바이트나 죽는 행 번호 같은 값은 실행할 때마다 조금씩 흔들린다. 그래서 판정은 '몇 배인가, 어떤 에러인가, 몇 줄인가'로 한다.
| 찾는 것 | 바로 가기 |
|---|---|
| 변경 벡터 → redo 레코드 → redo strand · WAL · Fast Commit | §1 redo |
| COMMIT은 무엇을 쓰나 · dirty는 왜 남나 | §2 commit |
| Undo의 3층: 트랜잭션 테이블 슬롯 · ITL · 행 lock byte | §3 undo 3층 |
| 블록 클린아웃 · 지연 클린아웃 · 01화 5,842의 정체 | §4 클린아웃 |
| CR 블록 생성 단계 (읽기 일관성) | §4.5 CR |
| ORA-01555의 원인 3종 · fetch across commit | §5 01555 |
| ORA-30036 vs ORA-01555: 반대쪽 두 장애 | §6 30036 |
| 커밋 주기: 무엇이 비싸지나 · log file sync | §7 커밋주기 |
| 반쯤 반영 (쪼갠 배치의 중간 실패) | §8 반쯤반영 |
| IMU (In-Memory Undo) | §9 IMU |
| 측정값 전부 | §10 측정 |
| Free에서 못 잰 것 | §11 한계 |
| 원천 좌표 | §12 좌표 |
1. redo — 변경 벡터 · WAL · Fast Commit
데이터를 바꾸는 작업은 모두 바꾸기 전에 그 변경을 로그에 먼저 적는다. 이것이 WAL(Write-Ahead Logging)이다. 일지가 먼저, 장부는 나중이다.
- 변경 벡터(change vector): "이 블록의 이 자리를 이렇게 바꿔라"를 담은 한 조각이다. DML 하나는 보통 변경 벡터를 두 개 이상 만든다. 데이터 블록을 바꾸는 벡터와, 그 되돌림 정보(undo)를 언두 블록에 적는 벡터다.
- redo 레코드(redo record): 원자적 변경 하나에 속한 변경 벡터들을 묶은 것이다. undo 블록을 바꾸는 것도 redo로 보호된다. 언두 자체도 복구 대상이기 때문이다.
- 로그 버퍼 → redo strand → 로그 파일: redo 레코드는 SGA의 로그 버퍼(이 컨테이너의
log_buffer는 약 4MB)에 모였다가 LGWR가 온라인 리두 로그 파일로 내려보낸다. 여러 세션이 동시에 redo를 만들면 로그 버퍼를 여러 가닥(redo strand)으로 나눠 경합을 줄인다.
실측 — 한 행 UPDATE (실습/02 [A])
| 무엇 | 값 |
|---|---|
| redo entries | 5 |
| redo size | 약 2,272바이트 |
| undo change vector size | 약 464바이트 |
| db block changes | 약 11 |
한 행을 고쳤는데 undo 변경 벡터도 함께 생긴다. 바꾼 값(redo)과 바꾸기 전 값(undo)을 동시에 적기 때문이다.
Fast Commit — 커밋이 빠른 이유
COMMIT은 바뀐 데이터 블록을 디스크에 쓰지 않는다. 그 트랜잭션의 redo를 로그 파일까지 밀어내고(flush), 언두 세그먼트 헤더의 트랜잭션 테이블 슬롯에 커밋 SCN을 적으면 끝난다. 쓰는 양이 작으니 빠르다. 이것을 Fast Commit이라고 한다.
2. COMMIT은 무엇을 쓰나
실측 — COMMIT 하나 (실습/02 [B])
| 무엇 | 값 |
|---|---|
| redo entries | 약 2 (커밋 레코드) |
| redo size | 약 1,212바이트 |
| undo change vector size | 약 172 |
| log file sync | +1 |
COMMIT 뒤에도 dirty가 남는다 (실습/02 [C] · ← 07화)
| 단계 | 그 객체의 dirty 버퍼 |
|---|---|
CREATE TABLE … AS SELECT 직후 | 4 (헤더) |
UPDATE + COMMIT 뒤 | 31 |
07화에서 넘겨받은 바로 그 31이다. COMMIT은 데이터 블록을 디스크에 쓰지 않는다. 그래도 안전한 이유는 두 가지다.
- 바꾼 내용은 이미 redo(일지)로 디스크에 적혀 있다(WAL). 인스턴스가 죽어도 redo를 다시 적용(roll forward)해 블록을 되살린다.
- 데이터 블록을 디스크로 내려보내는 것은 DBWn의 일이다. 체크포인트가 걸릴 때(또는 버퍼가 필요할 때) 비동기로 처리하므로 커밋과는 따로 움직인다.
그래서 "커밋했으니 디스크에 저장됐다"는 말은 틀렸다. 커밋이 보장하는 것은 redo가 디스크에 있다는 사실이지, 데이터 블록이 디스크에 있다는 것이 아니다.
3. Undo의 3층 — 트랜잭션 테이블 슬롯 · ITL · 행 lock byte
트랜잭션이 행을 잠그고 되돌리는 데 필요한 정보는 세 층으로 나뉘어 서로를 가리킨다. 12화에서 TX 이름표만 보고 넘어간 부분의 속 내용이다.
① 언두 세그먼트 헤더의 트랜잭션 테이블 슬롯 (트랜잭션 하나당 한 칸)
usn / slot / wrap = 12화의 TX 이름표. 상태(활성/커밋) · 커밋 SCN · 언두 시작 주소
▲ 가리킴
② 블록 머리의 ITL (Interested Transaction List) 칸 (이 블록을 건드리는 트랜잭션 목록)
Xid(위 슬롯을 가리킴) · Uba(이 변경의 언두 블록 주소) · Flag · Lck(잠근 행 수)
▲ 가리킴
③ 행 머리의 lock byte (이 행이 이 블록의 ITL 몇 번 칸에 묶였나)실측 — 블록 덤프로 ITL Flag 읽기 (실습/06 [B] · SYSDBA)
행 하나를 고치고 커밋하기 전에 블록을 덤프하면 다음과 같다.
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0005.018.0000099a 0x00009bee.011d.0a C--- 0 scn 0x0000000000639338
0x02 0x0004.016.000007f8 0x00009d18.0187.31 --U- 1 fsc 0x0000.00639346커밋한 뒤 다시 덤프하면 그 칸의 Flag가 --U- → C---로 바뀌고 커밋 SCN이 적힌다.
| 읽을 것 | 뜻 | 이 덤프 |
|---|---|---|
Xid | 트랜잭션 이름표. usn.slot.wrap(16진수) | 0x0004.016.000007f8 = usn 4 · slot 22 · wrap 2040 (12화의 그 이름표) |
Uba | Undo Block Address. 이 변경의 되돌림 슬립이 있는 언두 블록 | 0x00009d18.0187.31 |
Flag | --U- 활성(미커밋) · C--- 커밋됨 · ---- 빈 칸 | 둘째 칸이 --U- → 커밋 후 C--- |
Lck | 이 ITL 칸이 이 블록에서 잠근 행 수 | 1 (한 행을 고쳤다) |
Scn/Fsc | 커밋이면 커밋 SCN · 활성이면 Fsc(free space credit) | — |
꺼내는 법(트레이스는 컨테이너 안에 있다. Git Bash에서는
MSYS_NO_PATHCONV=1을 붙인다):docker exec ora-hanorder bash -c "grep -n -A3 'Itl' <Default Trace File 경로>"경로는 덤프 직후SELECT value FROM v$diag_info WHERE name='Default Trace File';로 확인한다.
그래서 락 에스컬레이션이 없다 (← 12화 §11)
행 락은 블록 안(행의 lock byte → ITL 칸)에 적힌다. 락 관리자 메모리에 행마다 적히는 것이 아니다. 10만 행을 잠가도 락 관리자가 아는 것은 트랜잭션 하나(슬롯 하나)뿐이라서 테이블 락으로 올릴 이유가 없다(12화 실측: 10만 행 UPDATE 뒤에도 V$LOCK은 2줄). 대신 대가가 있다. 블록마다 ITL 칸이 필요하고(ITL 부족 → 12화 §10), 그 표시를 나중에 누군가 지워야 한다(블록 클린아웃 → §4).
4. 블록 클린아웃 · 지연 클린아웃
트랜잭션이 커밋하면 그 트랜잭션이 건드린 블록들의 ITL에 커밋 SCN을 적어 'ITL을 닫아야' 한다. 이것이 블록 클린아웃이다.
| 종류 | 언제 | 비용 |
|---|---|---|
| commit cleanout (즉시) | 커밋 시점에 블록이 아직 버퍼 캐시에 있으면 그 자리에서 닫는다 | 커밋이 흡수한다 |
| 지연 클린아웃 (delayed) | 블록이 너무 많거나(캐시의 약 10% 초과) 커밋 전에 캐시에서 밀려나면 '열린 ITL'을 단 채 디스크로 간다. 다음에 그 블록을 읽는 세션이 닫는다 | 읽기가 redo를 쓴다 |
실측 — 지연 클린아웃 (실습/06 [A] · SYSDBA)
큰 UPDATE를 하고, 커밋하기 전에 그 블록들을 캐시에서 내보낸 뒤(실험에서는 FLUSH BUFFER_CACHE로 흉내 냈다) 커밋했다. 그다음 같은 SELECT를 두 번 돌렸다.
| 첫 읽기 | 둘째 읽기 | |
|---|---|---|
| redo size | 152,888바이트 | 2,076 |
| cleanouts only - consistent read gets | 1,694 | 0 |
| immediate (CR) block cleanout applications | 1,694 | 0 |
| consistent gets | 3,431 | 1,739 |
읽기만 했는데 첫 SELECT가 redo를 15만 바이트 쓰고 블록 1,694개를 청소했다. 둘째 SELECT는 깨끗하다. 밤 배치가 돈 다음 날 아침에 첫 조회가 유난히 느린 현상이 바로 이것이다.
01화 "설치 직후 첫 실행 5,842"의 정체 — 재측정 (설계와 달랐다)
개정계획에는 이 추가분이 "지연 클린아웃일 수 있다"고 적혀 있었다. 재 보니 아니었다.
T_ORD_BIG은INSERT /*+ APPEND */(직접 경로)로 적재한다. 직접 경로로 쓴 블록은 깨끗한 상태로 기록되므로 지연 클린아웃이 남지 않는다. 실제로 첫 읽기와 둘째 읽기의 consistent gets가 같았다(약 5,830).- 따라서 5,842 → 5,827의 추가분은 지연 클린아웃이 아니다. 첫 실행 때 드는 하드 파싱과 재귀 조회(딕셔너리, 동적 샘플링) 오버헤드다.
- 규칙 3 함정표의 'cold cache 첫 실행이 몇 블록 적게 찍힌다'(5,827→5,822)와도 다른 현상이다. 한쪽은 캐시가 차가워서 적게 찍히고, 다른 쪽은 첫 파싱이라 많이 찍힌다.
교훈: 가설이 '지연 클린아웃'이었더라도 직접 경로 적재는 지연 클린아웃을 남기지 않는다. 측정 결과가 설계와 다르면 측정을 따른다.
4.5 CR 블록 — 읽기 일관성이 만드는 사본
쿼리는 시작한 시점(스냅샷 SCN)의 데이터를 봐야 한다(읽기 일관성). 그 사이에 누군가 블록을 바꿔 커밋했다면, 오라클은 현재 블록(CURRENT)을 복사하고 그 사본에 언두를 적용해 스냅샷 시점으로 되돌린 CR 블록(Consistent Read 블록)을 만든다.
CR 블록 만드는 단계
1. 현재 블록을 버퍼 캐시에 복사한다 (→ 'CR blocks created')
2. 블록 ITL 을 보고, 스냅샷 SCN 이후에 커밋/활성인 변경을 찾는다
3. 그 변경의 Uba 로 언두 블록을 찾아, 되돌림을 적용한다 (→ 'data blocks consistent reads - undo records applied')
4. 스냅샷 SCN 시점의 모습이 될 때까지 2~3 반복- 07화에서 본
V$BH.status = 'cr'사본이 바로 이것이다. - 3번 단계에서 필요한 언두 블록이 이미 재사용돼 사라졌다면 ORA-01555가 난다(§5).
- 지연 클린아웃된 블록을 읽을 때도 ITL을 되살리려고 언두를 보는데, 그 언두가 없으면 역시 01555가 난다.
5. ORA-01555의 원인 3종
ORA-01555 "snapshot too old"는 읽기 일관성에 필요한 과거 언두가 이미 사라졌다는 뜻이다. 언두가 사라지는 경로는 세 가지다.
| # | 원인 | 무엇이 | 이 화에서 |
|---|---|---|---|
| 1 | 롤백 세그먼트(언두)를 덮어씀 | 긴 쿼리가 도는 동안 다른 트랜잭션(또는 자기 자신)이 커밋하면서, 그 쿼리가 써야 할 언두를 재사용해 덮어썼다 | 실습/04 실측 (에러 메시지의 rollback segment … too small) |
| 2 | 트랜잭션 테이블 슬롯 재사용 | CR 블록을 만들려면 "그 트랜잭션이 언제 커밋됐나"를 언두 세그먼트 헤더의 슬롯에서 봐야 하는데, 그 슬롯이 다른 트랜잭션에 재사용돼 과거 정보가 사라졌다 | 개념 (작은 언두·많은 트랜잭션에서 발생) |
| 3 | 지연 클린아웃 중 | 지연 클린아웃된 블록을 읽으며 "그 트랜잭션이 커밋됐나"를 확인하려는데, 그 트랜잭션의 슬롯/언두가 이미 사라져 커밋 시점을 구하지 못한다 | §4의 연장 |
fetch across commit — 왜 자기 발등을 찍나
sql
FOR r IN (SELECT ... FROM T ORDER BY ...) LOOP -- 긴 커서: 스냅샷 SCN 은 OPEN 시점
UPDATE T SET ... WHERE ...; -- 읽는 그 테이블을 고치고
IF MOD(n,N)=0 THEN COMMIT; END IF; -- 커밋 → 방금 쓴 언두를 '재사용 가능'으로 푼다
END LOOP;커서의 스냅샷은 OPEN 시점에 고정된다. 그런데 루프가 그 테이블을 고치고 커밋하면, 커서가 나중에 읽을 블록의 과거 언두가 그 커밋 때문에 풀려 버린다. 커서가 그 블록에 닿아 CR 블록을 만들려고 할 때 언두가 없으면 ORA-01555가 난다. 읽는 사람과 지우는 사람이 같은 셈이다.
실측 — 두 가지 모양 (실습/04)
| 모양 | 어떻게 재나 | 결과 |
|---|---|---|
| 동시 커밋이 과거를 지운다 | 긴 읽기 커서(정렬 없이 흘려 읽기) + 그 사이 다른 세션이 같은 테이블을 계속 UPDATE·COMMIT | 긴 읽기가 약 200행째에서 ORA-01555 (두 번 모두 재현) |
| 자기 커밋이 과거를 지운다 | 인덱스 순서로 읽으며 행을 고치고 커밋 (단일 세션) | 이 버전, 이 데이터에서는 잘 안 난다(아래 주의 참고) |
⚠️ 단일 세션에서 스스로 발등을 찍는 경우(self-inflicted)는 이 버전에서 재현이 들쭉날쭉했다. 한빛유통 정산 데이터는
CUS_ID(=MOD(SEQ,2000))가 물리 순서와 상관돼 있어서, 인덱스 순서로 읽어도 같은 블록을 다시 만나는 간격이 좁았다. 그래서 언두가 아직 남아 있는 경우가 많았다. 교재에서 확실하게 재현할 때는 두 세션의 동시 커밋을 쓴다. 긴 읽기를 돌리는 동안 다른 세션이 그 테이블을 고치는 방식이다. 커밋이 긴 읽기의 과거를 지운다는 메커니즘은 같다. → §11그래서 처방은 커밋 주기가 아니라 '읽기와 쓰기 분리'다. 목록을 먼저 담고 커서를 닫은 뒤 쓰거나, 키 범위로 쪼개 조각마다 자기 스냅샷을 갖게 하거나, 집합 연산(14화)으로 바꾼다. 커밋 간격을 늘려도 긴 커서가 열려 있는 한 과거는 계속 풀린다.
6. ORA-30036 vs ORA-01555 — 반대쪽 두 장애
| ORA-30036 | ORA-01555 | |
|---|---|---|
| 메시지 | unable to extend segment … in undo tablespace | snapshot too old: rollback segment too small |
| 모자란 것 | 지금 쓸 언두 공간 (활성 트랜잭션이 다 쥠) | 과거 시점의 언두 (이미 재사용됨) |
| 피해자 | 지금 쓰는 트랜잭션 | 오래 읽는 커서/쿼리 |
| 커밋을 덜 하면 | 가까워진다 | 멀어진다 |
| 커밋을 더 하면 | 멀어진다 | 가까워진다 |
| 언두를 늘리면 | 풀린다 | 안 풀린다 (사라진 과거는 안 돌아온다) · retention을 늘려야 한다 |
| 한 줄 | "되돌릴 공간이 없다" | "되돌릴 과거가 없어졌다" |
실측 — 같은 작은 언두(6M)에서 (실습/03 · 04)
| 배치 | 커밋 | 결과 |
|---|---|---|
| 보정 배치 [A] | 한 번만 | ORA-30036 · 약 42,000행째 (강도현 2020.07 재현) |
| 보정 배치 [B] | 1,000건마다 + 긴 읽기 동시 | ORA-01555 · 약 200행째 |
커밋 주기 하나만으로는 두 벽 사이를 빠져나갈 수 없다. 한쪽 벽(30036)을 피하려고 커밋을 당기면 다른 벽(01555)에 붙는다. 길은 읽기와 쓰기를 떼어 내는 것뿐이다. ORA-01555가 'retention' 문제라면
UNDO_RETENTION이나RETENTION GUARANTEE로 미룰 수는 있지만, fetch across commit 구조는 그대로 남는다.
7. 커밋 주기 — 무엇이 비싸지나
실측 — P1 적재 10만 건, 커밋 주기만 바꿔서 (실습/01)
| 한 번(once) | 1,000건 | 매 건 | 1,000건 + WRITE WAIT | |
|---|---|---|---|---|
| user commits | 3 | 102 | 100,002 | 102 |
| redo size | 약 3,000만 | 약 3,000만 | 약 5,700만 | 약 3,000만 |
| redo entries | 약 103,000 | 약 102,000 | 약 147,000 | 약 102,000 |
| undo change vector size | 약 680만 | 약 680만 | 약 1,360만 | 약 680만 |
| session logical reads | 약 110,000 | 약 110,000 | 약 373,000 | 약 110,000 |
| log file sync (내 세션) | 2 | 2 | 2 | 101 |
LGWR log file parallel write | 약 50 | 약 120 | 약 650 | 약 115 |
이 표에서 읽을 것:
- 한 번 ≈ 1,000건. redo와 undo가 거의 같다. 덩어리가 너무 작지만 않으면 끊는 비용은 거의 보이지 않는다.
- 매 건 커밋은 과하다. redo 약 1.9배, undo 약 2배, 논리 읽기 약 3.4배, LGWR 기록 약 13배다. 커밋할 때마다 커밋 레코드 redo와 블록 변경이 늘고, 로그도 더 자주 내려보낸다.
- log file sync의 함정. PL/SQL 루프 안의 COMMIT에서는 세션이 매번 기다리지 않는다. 10만 번 커밋해도
log file sync는 2회였다. 대신 LGWR의log file parallel write가 는다(50→650). 그런데COMMIT WRITE WAIT를 쓰면(또는 클라이언트가 건마다 커밋하면) 커밋마다log file sync를 기다린다(101). 행 단위로 커밋하는 애플리케이션이 느린 진짜 이유가 이것이다.
log file sync와log file parallel write는 짝이다: 앞의 것은 커밋한 세션이 'LGWR가 내 redo를 다 썼나'를 기다린 시간이고, 뒤의 것은 LGWR가 실제로 디스크에 쓴 시간이다.log file sync가 크면 LGWR 쪽(I/O, CPU)을 살펴보거나 커밋 횟수를 줄인다(V$SESSION_EVENT/V$SYSTEM_EVENT).
8. 반쯤 반영 — 원자성의 대가
실측 — 같은 배치가 50,000행째에서 장애 (실습/07)
| 커밋 | 반영된 건 | 그대로인 건 |
|---|---|---|
| 한 번만 | 0 | 100,000 |
| 1,000건마다 | 50,000 | 50,000 |
- 한 번 커밋은 원자적이다. 중간에 죽으면 ROLLBACK이 전부 되돌린다(전부 아니면 전무).
- 쪼갠 커밋은 중간에 죽으면 이미 커밋한 만큼 남는다(반쯤 반영). 다시 돌리면 그 부분을 또 처리할 수 있다(이중 보정).
- 쪼갤 거라면 재시작 지점을 설계한다. 어디까지 했는지 적어 두고(체크포인트 테이블), 다시 돌릴 때 멱등하게 처리하거나 이미 한 부분은 건너뛰게 짠다. → 「정합성 회귀 체크리스트」 E(경계: 반쯤 반영)·H(영향: 재실행).
9. IMU — In-Memory Undo
이 인스턴스는 IMU가 켜져 있다(_in_memory_undo = TRUE). IMU는 작은 트랜잭션의 언두와 redo를 공유 풀의 전용 메모리 풀에 잠깐 모았다가, 커밋하거나 플러시할 때 언두 세그먼트와 로그 버퍼로 한꺼번에 내보낸다. 그만큼 라치를 잡는 횟수와 redo를 기록하는 횟수가 준다.
- 흔적은
V$SYSSTAT의IMU commits,IMU Flushes,IMU undo allocation size에서 볼 수 있다. - 실측에서는 매 건 커밋 쪽의
IMU commits가 크게 잡혔다. 작은 커밋이 IMU 경로를 많이 탔다는 뜻이다. - IMU가 있으면 단일 세션 self-inflicted ORA-01555를 재현하기가 더 어렵다. 언두를 메모리에 더 오래 쥐고 있어서 과거가 덜 사라지기 때문이다. → §5 주의 · §11
10. 이 화의 측정값 전부
커밋 주기 (실습/01 · P1 적재 10만 건)
| 지표 | once | 1000 | each | 1000+WAIT |
|---|---|---|---|---|
| user commits | 3 | 102 | 100,002 | 102 |
| redo size | ~3,002만 | ~3,001만 | ~5,717만 | ~3,002만 |
| undo change vector size | ~680.9만 | ~681.4만 | ~1,362만 | ~681.4만 |
| session logical reads | ~110,221 | ~110,509 | ~373,014 | ~110,658 |
| log file sync (세션) | 2 | 2 | 2 | 101 |
| LGWR log file parallel write | ~50 | ~120 | ~650 | ~115 |
변경 벡터 · 커밋 · dirty (실습/02)
| 무엇 | 값 |
|---|---|
| 한 행 UPDATE | redo entries 5 · undo change vector 약 464바이트 · db block changes 약 11 |
| COMMIT 하나 | redo 약 1,212바이트 · undo 약 172 · log file sync +1 |
| dirty: CTAS 직후 → UPDATE+COMMIT 뒤 | 4 → 31 (← 07화) |
ORA-30036 / ORA-01555 (실습/03 · 04 · 작은 언두 6M · retention 1)
| 무엇 | 값 |
|---|---|
| 한 번 커밋 배치 | ORA-30036 · 약 42,000행째 |
| 긴 읽기 + 동시 커밋 | ORA-01555 · 약 200행째 (2회 재현 동일) |
| 원복 확인 | undo_tablespace UNDOTBS1 · undo_retention 900 · UNDO_C13 삭제 |
지연 클린아웃 · ITL (실습/06 · SYSDBA)
| 무엇 | 값 |
|---|---|
| 지연 클린아웃 첫 읽기 vs 둘째 | redo 152,888 → 2,076바이트 · cleanouts 1,694 → 0 |
| ITL Flag | --U-(활성·Lck 1) → C---(커밋·SCN) · ----(빈 칸) |
| ITL Xid | 0x0004.016.000007f8 = usn 4 · slot 22 · wrap 2040 |
| APPEND 적재 첫 읽기 | 지연 클린아웃 없음 (5,830 ≈ 5,833). 01화의 5,842는 지연 클린아웃이 아니다 |
반쯤 반영 (실습/07)
| 커밋 | 반영 | 그대로 |
|---|---|---|
| 한 번 | 0 | 100,000 |
| 1,000건 | 50,000 | 50,000 |
측정 환경
Oracle AI Database 26ai Free Release 23.26.2.0.0 · cpu_count 2 · db_block_size 8192 · undo_management AUTO · undo_tablespace UNDOTBS1 · undo_retention 900 · PDB 로컬 언두(LOCAL_UNDO_ENABLED TRUE) · _in_memory_undo TRUE · log_buffer 약 4MB · 온라인 리두 로그 10MB × 2.
11. Free에서 못 잰 것
| 못 잰 것 | 왜 | 대신 한 것 |
|---|---|---|
| 단일 세션 self-inflicted ORA-01555 (안정 재현) | IMU + 이 데이터의 CUS_ID–물리 상관 때문에 들쭉날쭉 | 두 세션(긴 읽기 + 동시 커밋)으로 확정 재현 (§5) |
| 수백 세션이 공유 언두를 압박하는 규모 | Free CPU 2개 · 작은 SGA | 작은 언두(6M)로 메커니즘만 재현 |
NOCACHE·대용량 PGA 소트가 TEMP·언두에 주는 압박 | 작은 PGA | — |
| ORA-01555 재현에 쓴 작은 언두 | 운영에선 불가 | SYSDBA 스크립트로 분리 · 끝에서 반드시 원복 + 원복 확인 |
언두를 작게 바꾸는 것은 인스턴스 설정 변경이라 SYSDBA 전용이다. 이 화의 본체(커밋 주기, redo/undo, log file sync, 반쯤 반영)는
실습/01·02·07로 SYS 없이 끝난다. 01555, 30036, 지연 클린아웃만실습/03·04·06(SYSDBA)에서 다룬다. 대규모 EE 환경은 별권에서 다룬다.
12. 원천 좌표
전체 지도 →
00_자료준비/06_원서와_공식문서_지도.mdPDF 페이지 = 책 페이지 − 2 ·성능고도화I_목차색인.md(공개 사이트에는 없음)
성능고도화 I권 Ch1 후반 (주 원천)
| 주제 | 책 페이지 |
|---|---|
| Redo의 역할 · Redo 로그 버퍼 · Log Writer | 26 · 28 |
| Undo 세그먼트 · 트랜잭션 테이블 슬롯 · Lock Byte (← 12화가 넘긴 41·46) | 41 · 46 |
| Fast Commit · Redo Strand | 48 · 52 |
| 블록 클린아웃 (Commit Cleanout · Delayed Cleanout) | 54 |
| CR(Consistent Read) 블록과 읽기 일관성 | 58 |
오라클 공식문서
| 주제 | 매뉴얼 | 장 제목 |
|---|---|---|
| Redo · Undo · Data Concurrency and Consistency (읽기 일관성 · ORA-01555) | cncpt | Data Concurrency and Consistency · Memory Architecture |
COMMIT · COMMIT WRITE | sqlrf | COMMIT |
UNDO_RETENTION · UNDO_TABLESPACE · Undo 관리 | admin / refrn | Managing Undo · Initialization Parameters |
V$MYSTAT · V$SESSTAT · V$SESSION_EVENT · V$SYSTEM_EVENT · V$TRANSACTION · V$UNDOSTAT | refrn | Dynamic Performance Views |
| 블록 덤프 · 트레이스 · ADR | admin | Managing Diagnostic Data |
이 화에서 꼭 확인할 뷰·통계
sql
V$MYSTAT / V$STATNAME -- redo size · undo change vector size · user commits · redo entries
-- consistent gets - examination · data blocks consistent reads - undo records applied
-- cleanouts only - consistent read gets · immediate (CR) block cleanout applications
-- CR blocks created · commit cleanouts · IMU commits
V$SESSION_EVENT -- log file sync (내 세션) ↔ log file parallel write (LGWR 세션)
V$TRANSACTION -- xidusn · xidslot · xidsqn (언두 세그먼트 헤더 슬롯)
V$BH -- status(cr · xcur) · dirty
V$UNDOSTAT -- tuned_undoretention · maxquerylen다음 화가 여기서 가져가는 것
| 화 | 가져가는 것 |
|---|---|
| 14 | "그럼 배치를 어떻게 빠르고 안전하게 돌리나": 집합 연산(INSERT…SELECT·MERGE) · APPEND · 파티션 · 병렬. P1을 다시 쓴다 (§5·§7) |
| 14 | 04화 인덱스와 05화 HWM의 적재 비용 청구서. redo/undo로 잰다 (§1) |
| 15 | PL/SQL 루프 자체의 비용(컨텍스트 스위칭): 한 건씩 도는 구조 (§7) |
| 16·17 | V$는 장애가 끝나면 사라진다. redo/undo/대기의 지나간 값을 보는 도구 (AWR·ASH) |
| 18 | 「SQL 코딩 가이드」·「커밋 주기 기준」: 채번과 배치는 읽기와 쓰기를 분리한다 (근거 2회: 12·13화) |