Skip to content

13화 · 안전 조치의 대가 ​

🗓️ 5월 27일 (월) · 12화에서 일주일 뒤 · 무대 ora-hanorder오늘 배울 것: 커밋 주기만으로는 두 장애를 다 피할 수 없다. 그리고 지난주에 내가 낸 처방이 주말에 장애로 돌아왔다. 오늘 다루는 조각은 처음 등장하는 P1(정산 적재 배치)이다. 고치지는 않고, 재어 보고 근거를 남긴다.


파일 세 개 ​

파일무엇을 하나시간
읽는다1_사건.md1,000건 커밋 처방이 ORA-01555를 부른 날. 방향이 반대인 장애 두 개를 재 보고, 정정 전 사본함 앞에 서고, 블록 덤프로 ITL을 읽고, 지난주에 비워 둔 칸을 채우는 하루35분
한다3_실습과_기록.md커밋 주기 측정 · 변경 벡터 · ORA-30036·01555 직접 재현 · 지연 클린아웃 · 블록 덤프 · 반쯤 반영 · 커밋 주기 기준90~120분
찾는다2_파고들기.mdredo·WAL·Fast Commit · Undo 3층(슬롯·ITL·lock byte) · CR 블록 · 01555 원인 3종 · 30036 대조 · 커밋 주기 · IMU필요할 때

1을 읽고 3을 따라 한다. 2는 막힐 때 펼친다. 시간이 없는 주에는 1_사건을 중단 지점 1까지 읽고 3_실습은 STEP 0, 1, 2, 6, 8만 한다(약 50분, SYS 계정이 없어도 된다).


지난 화에서 들고 오는 것 ​

12화 포스트모템 1호 재발방지 2번"주간 보정 배치: 트랜잭션을 짧게. 배치는 N건마다 COMMIT" · 「리스크」 칸이 비어 있다
오과장의 답"P1처럼 1,000건 단위로 다음 주부터 적용하겠다"
05화가 넘긴 것블록의 ITL 칸이 모자라면 어떻게 되나 · 칸에 무엇이 적히나
07화가 넘긴 것COMMIT 뒤에도 남은 dirty 31 · 커밋이 무엇을 쓰기에 안전한가(Redo·WAL)
12화가 넘긴 것TX 이름표(usn·slot·wrap)가 가리키는 언두 세그먼트 헤더의 칸 · 블록 ITL · 행 lock byte

그리고 오과장에게서 메신저가 한 줄 와 있다.

"주말 보정 배치가 토요일 새벽에 죽었어요. ORA-01555. 1,000건마다 커밋으로 바꾸고 나서 처음 돌린 날."


오늘 여는 코드 ​

sql
-- P1_rev00 · 강도현 — 야간 정산 적재 배치 (리비전 파일에 처음 남는다)
IF MOD(n, 1000) = 0 THEN COMMIT; END IF;   -- 트랜잭션은 짧게 (2020.07 강도현)
sql
-- 주간 보정 배치 (오과장, 12화 처방 적용) — 토요일에 죽은 것
FOR r IN (SELECT ... FROM 정산 ORDER BY ...) LOOP   -- 긴 커서
  UPDATE 정산 SET ... WHERE ...;                     -- 읽는 그 테이블을 고치고
  IF MOD(n,1000)=0 THEN COMMIT; END IF;              -- 커밋 → 과거를 풀어 준다 = fetch across commit
END LOOP;

매주 처음 하는 일(diff)이 오늘은 내가 쓴 처방을 되짚는다. "짧게 하라"는 맞는 말이었다. 다만 ORA-30036에만 맞았다. 바로 그 '짧게'가 ORA-01555를 불렀다.


오늘의 전개 — 두 번 틀린다 ​

내가 믿은 것측정
09:40"언두가 부족한 거다. 늘리면 된다"한 번 커밋하면 ORA-30036(언두 부족, 맞다). 그런데 1,000건마다 커밋하면 ORA-01555(반대쪽). 언두를 늘려도 01555는 안 풀린다
10:20"그럼 더 자주 커밋하면 싸지겠네"매 건 커밋하면 redo 약 1.9배 · undo 약 2배 · LGWR 약 13배. 더 비싸지고 01555도 더 빨리 난다
11:10(비유) 정정 전 사본함: 완료 도장이 사본함을 비운다드물게 찍으면 함이 꽉 차고(30036), 자주 찍으면 과거가 사라진다(01555)
13:00"커밋하면 디스크에 저장된다"dirty 31(← 07화). 커밋은 redo(WAL)만 디스크로 민다. 블록은 DBWn이 나중에 쓴다
14:00(측정) 블록 덤프로 본 ITL--U-(활성) → C---(커밋). Undo 3층이 서로를 가리킨다(← 12화)
15:00"5,842는 지연 클린아웃이다" (01화 가설)아니다. APPEND 적재에는 지연 클린아웃이 없다. 추가분은 첫 파싱과 재귀 조회다
16:30"그럼 1,000 말고 5,000건?"주기 문제가 아니다. fetch across commit이 문제다 → 읽기와 쓰기를 떼어 낸다

왜 이렇게 짰을까? 3막은 "고친 것이 다음 문제를 만드는" 막이다. 12화에서 '나'가 낸 재발방지책이 13화의 장애가 된다. 그리고 그 장애의 뿌리는 빈칸 하나, 리스크를 적지 않은 데 있다.


오늘 배우는 한 문장 ​

커밋 주기만으로는 두 장애를 다 피할 수 없다. 드물게 하면 ORA-30036, 자주 하면 ORA-01555다. 진짜 문제는 주기가 아니라 읽으면서 고치고 커밋하는 구조(fetch across commit)다. 읽기와 쓰기를 떼어 내야 한다.

커밋을 덜커밋을 더언두를 늘리면
ORA-30036 (지금 쓸 언두 없음)가까워진다멀어진다풀린다
ORA-01555 (과거 언두 사라짐)멀어진다가까워진다안 풀린다

🔑 오늘의 비유 — 총무팀 정정 전 사본함 ​

11화의 결재 서식함, 12화의 발번 대장을 지나 이번에는 총무팀 데스크 옆 캐비닛이다. 장부를 고치기 전에 원래 값을 슬립에 베껴 이 함에 넣는다(= undo). 한 묶음을 끝내면 완료 도장(= COMMIT)을 찍고, 도장이 찍힌 슬립은 버려도 된다(재사용 가능).

정정 전 사본함오라클
고치기 전 값을 슬립에 베껴 함에 넣는다undo: 바꾸기 전 값(롤백과 과거 읽기에 쓴다)
완료 도장을 찍으면 그 슬립은 버려도 된다COMMIT: 그 트랜잭션의 언두는 재사용할 수 있다
도장을 안 찍고 다 고치면 함이 꽉 찬다한 번 커밋 → ORA-30036
도장을 자주 찍어 함을 비우는데, 긴 감사가 '시작할 때의 장부'를 찾으면 슬립이 없다fetch across commit → ORA-01555
고치기 전에 출납 일지에 먼저 적는다redo / WAL: 일지가 먼저, 장부는 나중
도장을 찍어도 장부는 아직 책상 위에 있다dirty: 커밋해도 블록은 메모리에 있다(← 07화)

☠️ 오늘의 함정 — "짧게 하라"를 모든 배치에 똑같이 적용한다 ​

"트랜잭션을 짧게"는 한 번에 다 커밋하는 큰 트랜잭션(ORA-30036 위험)에는 맞는 처방이다. 그런데 읽으면서 고치고 커밋하는 배치(fetch across commit)에 그대로 적용하면, 그 짧은 커밋들이 긴 커서의 과거를 지워 ORA-01555가 난다.

같은 처방맞는 곳틀리는 곳
"N건마다 COMMIT"읽는 테이블 ≠ 고치는 테이블 (P1: T_ORD를 읽어 빈 테이블에 적재)읽는 테이블 = 고치는 테이블 (보정 배치: 긴 커서로 읽으며 고친다)

처방은 코드의 '모양'을 보고 고른다. 같은 문장이 어떤 배치에서는 안전하고 다른 배치에서는 장애가 된다. → 「커밋 주기 기준」


실습 준비 ​

bash
docker start ora-hanorder

12화를 마친 상태에서 시작한다. 실습/00_준비와_점검.sql이 이 상태를 확인한다(T_ORD_BIG 100만 건, 인덱스 3개 · T_ORD_STLM 10만 건 · LAST_NO 0 · PKG_ORD_STLM VALID).

PowerShell에서는 먼저 $OutputEncoding = New-Object System.Text.UTF8Encoding $false 한 줄을 실행한다.

스크립트무엇계정
실습/00_준비와_점검.sql전제 확인 · 복사본·배치 함수·수첩sqlp
실습/01_P1_커밋주기.sql★ 커밋 주기별 redo·undo·log file sync (본체)sqlp
실습/02_변경벡터와_커밋.sql변경 벡터 · COMMIT · dirty 31 (WAL)sqlp
실습/03_작은언두_ORA30036_SYSDBA.sql작은 언두 + 한 번 커밋 → ORA-30036SYS
실습/04_churner.sql·04_reader.sql★ 긴 읽기 + 동시 커밋 → ORA-01555sqlp (두 세션)
실습/05_원복_SYSDBA.sql★★ 언두 원복. 반드시 돌린다SYS
실습/06_지연클린아웃과_ITL_SYSDBA.sql지연 클린아웃 · 블록 덤프 ITLSYS
실습/07_반쯤_반영.sql쪼갠 배치가 중간에 실패하면 반쯤 반영sqlp
실습/09_정리.sql_C13 삭제 · 12화 상태 · 언두 원복 확인sqlp

어느 스크립트든 몇 번을 돌려도 결과와 판정이 같다(두 번씩 돌려 확인했다). redo 바이트나 죽는 행 번호는 조금씩 흔들리므로 '몇 배인가, 어떤 에러인가, 몇 줄인가'로 읽는다. ⚠️ 03을 돌렸으면 반드시 05로 원복한다. 03은 인스턴스의 언두를 작게 바꾼다. 본체(01, 02, 07)는 SYS 없이 끝난다. ℹ️ Docker로 띄운 Oracle Database Free 한 대에서 모든 실습을 마친다. 수백 세션이 공유 언두를 압박하는 규모는 이 컨테이너로 만들 수 없어서, 작은 언두(6M)로 메커니즘만 쟀다. 대규모 EE 환경은 별권에서 다룬다.


이 화의 산출물 ​

무엇어디
P1_rev00: 강도현 원본 · 2020.07 근거 · 고치지 않는다(재어 보고 근거만 남긴다. 전진은 14화)리비전/P1_rev00.sql
「커밋 주기 기준」 ★ 새 표준. 집합 연산 → fetch across commit → 언두 크기 순으로 묻는 결정표40_보고서와_표준/템플릿/커밋주기_기준.md
T4 포스트모템 2호 (+T1): 토요일 보정 배치 ORA-01555 · 반쯤 반영 (정합성 E·H)3_실습과_기록.md STEP 8
12화 리스크 칸 회수: 포스트모템 1호 재발방지 2번의 빈칸을 채우는 장면1_사건.md 오후 5시 30분 · 3_실습 STEP 7

이 화가 다음으로 넘기는 것 ​

무엇어디로
"배치를 어떻게 빠르고 안전하게 돌리나": 집합 연산(INSERT…SELECT·MERGE) · APPEND · 파티션 · 병렬 · P1 다시 쓰기14화
04화 인덱스와 05화 HWM의 적재 비용 청구서. redo/undo로 잰다14화
PL/SQL 루프 자체의 비용(컨텍스트 스위칭): 한 건씩 도는 구조15화
V$는 장애가 끝나면 사라진다. redo/undo/대기의 지나간 값은 어디서 보나16·17화
「SQL 코딩 가이드」·「커밋 주기 기준」: 채번과 배치는 읽기와 쓰기를 분리한다 (근거 2회 · 12·13화)18화

← 12화 「아무도 안 건드렸는데 전체가 멈췄다」 · 교재 전체 안내 · 다음 → 14화 「야간 배치가 아침을 넘긴다」