테마
14화 · 실습과 기록
여기서 하는 일: 일요일 밤 체인을 단계별로 재현하고, 청구서를 정산하고, 무기와 그 대가를 재고, P1을 한 문장으로 바꾸고, 파티션의 칸막이를 세어 보고, T3 배치 SLA 리포트 1호를 쓴다.
1_사건.md를 읽은 것은 "선배가 하는 걸 지켜본 것"이고, 이 문서를 끝내야 직접 해 본 것이 된다. 개념이 막히면2_파고들기.md를 펼친다.시간이 없는 주에는 STEP 0 → 1 → 3(03만) → 5 → 10만 한다(약 50분). 오래 걸리는 01, 02와 버거원을 빼도 본체는 끝난다.
이 화의 실습 지도
| 스크립트 | 무엇 | 컨테이너 · 계정 | 소요 |
|---|---|---|---|
실습/00_준비와_점검.sql | 13화 종료 상태 확인 · 스냅샷 수첩 · 파티션 복사본 만들기 프로시저 | ora-hanorder · sqlp | 수 초 |
실습/01_청구서_정산.sql | 인덱스 0~4개 적재 비용 · T2-002 · FK·트리거 · 순번 키 역순 | 〃 | 15~20분 |
실습/02_재적재_운영그대로.sql | 인덱스 3개를 켠 채 일반 INSERT로 100만 건(선택) | 〃 | 5~10분 |
실습/03_재적재_무기.sql | ★ APPEND · 끄고 켜기 · NOLOGGING · 꺼진 동안의 Q1 · 온라인 통계 | 〃 | 40초 |
실습/04_APPEND의_대가.sql | 배타 락 · ORA-12838 · 빈 칸 무시 · 파티션 + APPEND | 〃 | 30초 |
실습/05_P1_집합처리.sql | ★ P1 rev00 · rev01 · rev02 · 정합성 · 남의 눈 | 〃 | 5초 |
실습/06_월말정리_DELETE와_파티션.sql | DELETE · SHRINK · DROP PARTITION · GLOBAL 인덱스 | 〃 | 10초 |
실습/07_Pruning_한빛.sql | Pstart/Pstop · 키 가공 · LOCAL Prefixed/Nonprefixed | 〃 | 10초 |
실습/08_병렬_강등.sql | PARALLEL(8)이 무시되는 증거 다섯 | 〃 | 수 초 |
실습/09_버거원_파티션.sql | 유형별 Pruning · 크기 · Nonprefixed PK · 이관 스크립트의 병렬도 | ora-burger · dev | 수 초 |
실습/10_버거원_공간회수와_재설계.sql | ★★ ORD_ITEM_LIST_HASH 삭제 · 같은 설계 다시 짓기 | 〃 | 1분 |
실습/99_정리.sql | C14 · P14 삭제 · 13화 종료 상태 확인 | ora-hanorder · sqlp | 수 초 |
PowerShell에서는 먼저
$OutputEncoding = New-Object System.Text.UTF8Encoding $false를 한 줄 실행한다. 실행 명령은 각 스크립트 머리에 적힌 두 줄(Git Bash / PowerShell)을 그대로 쓴다. 예를 들면 다음과 같다.bashdocker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/03_재적재_무기.sql모든 스크립트는 몇 번을 돌려도 결과가 같다(두 번씩 돌려 확인했다). 원본
T_ORD_BIG,T_ORD,T_ORD_STLM,PKG_ORD_STLM에는 한 줄도 쓰지 않고, 전부_C14,_P14복사본에서 작업한다. 경과 시간(초)은 장비 사정을 타므로 redo, 블록 변경, Buffers로 대조한다.
STEP 0 — 직전 리비전을 연다 · 전제를 확인한다 (5분) ★ 매주 처음 하는 일
bash
docker start ora-hanorder
cat 리비전/P1_rev00.sql # 13화가 남긴 메모 : "전진은 측정이 정당화할 때(14화)"bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/00_준비와_점검.sql| 확인 | 기대 |
|---|---|
| [전제] big_rows · big_idx · hist · stlm_rows · last_no · pkg · undo_ts | 1000000 · 3 · FREQUENCY · 100000 · 0 · VALID · UNDOTBS1 |
| 데이터 지문 | 255,398,623,700 |
| 로그 모드 | NOARCHIVELOG · NO (← STEP 3에서 이 차이를 읽는다) |
| 만든 것 | 무효 0 |
버거원을 쓸 거면 (STEP 8) — 먼저 자리를 만든다
버거원 DB는 파티션 복사본까지 넣으면 Free의 12GB 한도에 바짝 붙는다. 그래서 14화를 시작하기 전에 적재용 스테이징 테이블 두 개를 지운다(L2 카드 「공간이 빠듯할 때」 참고. 2~4.sql로 언제든 다시 만들 수 있다).
bash
docker start ora-burger
docker exec -i ora-burger sqlplus -s dev/dev@localhost/FREEPDB1 <<'SQL'
DROP TABLE ORD_TEMP PURGE;
DROP TABLE ORD_TEMP2 PURGE;
SQL
# PowerShell: @" ... "@ here-string 에 두 줄을 넣어 흘린다. 한 줄에 두 문장을 이어 쓰면 ORA-03405 로 둘 다 안 지워진다이미 지워져 있으면
ORA-00942가 나온다. 정상이다.
STEP 1 — 예측을 봉인한다 (5분)
Q1. 인덱스 3개 켠 채 100만 건 재적재. 그중 '리두 로그를 갈아 끼우느라 기다린 시간' 은 몇 %? ____
Q2. 5만 건 INSERT 의 redo — 인덱스 0개 2.44 MB. 3개면? ____ MB
Q3. APPEND 로 넣으면 인덱스를 켠 채여도 redo 가 거의 0 이 된다. 예 / 아니오
Q4. APPEND 로 넣고 커밋 전에 같은 테이블을 SELECT 하면? ______
Q5. 월별 파티션 + TRUNCATE + APPEND 재적재. 6월 한 칸 전체 스캔은 CTAS 직후(161)와 비교해? 같다 / 줄었다 / 늘었다
Q6. Free 에서 /*+ PARALLEL(8) */ 을 붙이면? 빨라진다 / 에러 / 아무 일도 없다▶ 적고 나서 펼치기
| 대부분의 답 | 실측 | |
|---|---|---|
| Q1 | "절반쯤" | 94~98%(329초 중 309.5초, 542초 중 532.2초 · log file switch (checkpoint incomplete)) |
| Q2 | "7~8MB" | 38.55MB(인덱스 하나에 약 12MB) |
| Q3 | "예" | 아니오. 켠 채면 268.8MB다. 인덱스까지 꺼야 0.2MB가 된다 |
| Q4 | "ORA-12838"(책) | 이 버전(23ai)에서는 읽힌다(1,001,000). 운영 버전이 다르면 책대로 막힌다 |
| Q5 | "같다" | 늘었다. 1,169(칸마다 빈 블록 1,006 · 첫 익스텐트 8MB) |
| Q6 | "빨라진다" | 아무 일도 없다. Hint Report U - Unused · 계획 해시 동일 |
STEP 2 — 청구서 정산 (20분): 02·04·08·09화가 넘긴 것
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/01_청구서_정산.sql⏱ 15~20분 걸린다(실측 19분). 인덱스 구성을 바꿔 가며 5만 건을 서른 번 가까이 넣었다 뺀다. 시간의 대부분은 리두 로그 교체 대기다.
| 인덱스 | redo | 블록 변경 | 논리 읽기 | ☐ |
|---|---|---|---|---|
| 0개 | 2.44MB | 약 1,920 | 약 2,760 | ☐ |
| 1개 | 14.13MB | 약 102,480 | 약 153,740 | ☐ |
| 2개 | 26.09MB | 약 201,060 | 약 301,960 | ☐ |
| 3개 | 38.55MB | 약 301,700 | 약 453,230 | ☐ |
| 4개 (08화 기각안) | 50.62MB | 약 397,310 | 약 596,980 | ☐ |
T2-002: 3개에서 (ORD_DT)만 뺀 2개 | 26.85MB | 약 201,130 | 약 302,060 | ☐ |
먼저 04화의
05_DML_청구서.sql이 낸 0개, 1개, 2개 값(2.44 · 14.13 · 26.10)과 같은지 본다. redo는 소수점까지 거의 같고, 블록 변경과 논리 읽기는 0.1% 안쪽으로 맞으며, 경과 시간(cs)은 크게 흔들린다.
눈으로 짚을 것
☐ 인덱스 하나가 5만 건에 블록 변경 약 10만 = 행당 2번. 무엇을 두 번 고치나? → 리프 블록 · 그 언두
☐ [제약·트리거] FK 를 붙이면 redo 는 그대로인데 논리 읽기만 늘었다 — 왜? → 행마다 부모 PK 를 찾아간다
☐ 행 트리거를 붙이면 redo 가 12 MB 남짓 는다 — 인덱스 하나 값. 왜? → 여러 행을 한 블록에 넣던 INSERT 가 한 행씩 넣는다
☐ [01-T1~T3] FK 나 트리거가 있으면 계획이 LOAD TABLE CONVENTIONAL · Note 에 이유
☐ [오른쪽 끝 블록] 보통 : 90-10 분할 107 · 역순 : 블록 변경 약 10만 · 범위 조건이 INDEX FULL SCAN 2,230STEP 3 — 재적재: 운영 그대로 vs 무기 (10분) ★
① 운영 그대로 (선택 · 5~10분)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/02_재적재_운영그대로.sql| 단계 | redo | 경과 | 그중 로그 교체 대기 | ☐ |
|---|---|---|---|---|
| ② 적재 + 커밋 | 916.6MB | 5~10분(실측 329 · 542 · 585초) | 그 대부분(실측 309.5 · 532.2 · 556.3초) | ☐ |
| ④ 통계 수집 | 1.4MB | 약 1초 | 0 | ☐ |
| 켠 채 채운 인덱스 크기 | 약 4,370 · 3,555 · 5,370~5,510(갓 만든 것은 2,653 · 3,206 · 3,344. 일반 INSERT의 분할 위치는 회차마다 조금 다르다) | ☐ |
10화는 같은 배치를 9분 13초로 쟀다. 시간은 흔들려도 redo 916.6MB는 흔들리지 않는다. 6분을 기다리기 어렵다면 건너뛰고 위 표의 숫자로 읽으면 된다.
② 무기 (실습/03 · 30초)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/03_재적재_무기.sql| 단계 | redo | 경과 | ☐ |
|---|---|---|---|
[B] APPEND · 켠 채 | 268.8MB | 8~11초(2회) | ☐ |
| [E] 일반 · 끄고 → 켜기 | 48.4 + 0.4MB | 약 1 + 5초(2회 같음) | ☐ |
[C] APPEND · 끄고 → 켜기 | 0.2 + 0.4MB | 약 0.25 + 4.7초 | ☐ |
[D] [C] + NOLOGGING | [C]와 같다 | ☐ |
눈으로 짚을 것
☐ [B] 직후 : NUM_ROWS 1000000 · NOTES STATS_ON_LOAD · 그런데 ORD_ST 의 HISTOGRAM 은? → NONE (원본 FREQUENCY)
→ "APPEND 로 바꾸면 통계 수집 단계를 빼도 된다" 는 틀렸다. 그 단계는 1초였다
☐ [C] 인덱스를 끄면 세그먼트가 0 이 된다 (UNUSABLE)
☐ [03-C1] 꺼진 동안 Q1 : TABLE ACCESS FULL 약 5,820 → [03-C2] 켠 뒤 : 91 ← 끄고 켜기의 대가
☐ 다시 켠 인덱스 = 갓 만든 크기 (2,653 · 3,206 · 3,344)
☐ [C] ≈ [D] — 왜 NOLOGGING 이 아무것도 안 바꾸나? → 이 DB 는 NOARCHIVELOG (2_파고들기 §5)STEP 4 — APPEND의 대가 (10분)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/04_APPEND의_대가.sql| 블록 | 기대 | ☐ |
|---|---|---|
| [1] 일반 INSERT | TM LMODE 3 · 다른 트랜잭션: 넣을 수 있다 | ☐ |
[1] APPEND | 계획 LOAD AS SELECT · TM LMODE 6 · 다른 트랜잭션: ORA-00054 | ☐ |
[2] 커밋 전 SELECT | 1,001,000(책의 ORA-12838은 이 버전에서 안 난다) | ☐ |
[3] DELETE 33만 → 되채움 | 일반 6,016블록 · 전체 스캔 5,889 / APPEND 7,990 · 7,862 | ☐ |
[4] 파티션 CTAS 직후 | 288MB · 6월 161 | ☐ |
[4] TRUNCATE → APPEND 뒤 | 576MB · 6월 1,169 · 6월 칸 full 160 · 빈 블록 1,006 | ☐ |
[4] INITIAL 64K로 같은 일 | 72MB · 6월 169 · 빈 블록 5 | ☐ |
☐ [1] 이 적재가 아침까지 가면 낮 업무는 이 테이블에 무엇을 못 하나?
☐ [3] 월말 DELETE + 일상 APPEND 테이블은 매달 어떻게 되나?
☐ [4] '칸막이를 하나만 연다(SINGLE 30)' 는 맞는데 왜 7배인가? — Pstart/Pstop 만 보고 보고하면 놓치는 것STEP 5 — P1을 한 문장으로 (10분) ★ 오늘의 조각
bash
cat 리비전/P1_rev01.sql 리비전/P1_rev02.sql
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/05_P1_집합처리.sql| 판 | 경과 | redo | undo | 커밋 | ☐ |
|---|---|---|---|---|---|
| rev00 루프 · 1,000건 커밋 | 약 0.9초 | 28.66MB | 6.50MB | 101 | ☐ |
rev01 INSERT … SELECT | 0.08~0.23초 | 4.47MB | 0.26MB | 2 | ☐ |
rev02 + APPEND | 약 0.11초 | 0.08MB | 0.02MB | 2 | ☐ |
커밋 수에는
TRUNCATE의 커밋 1이 들어 있다. redo는 회차마다 소수점 둘째 자리에서 조금 흔들린다.
정합성 — 체크리스트 B (다섯 개 전부)
| new_rows · old_rows | new_sum · old_sum | new−old · old−new | dup_seq | ☐ |
|---|---|---|---|---|
| 100000 · 100000 | 25,519,833,100 · 25,519,833,100 | 0 · 0 | 0 | ☐ |
☐ seq_order_diff = 0 — 정산번호가 같은 건 '맞아서' 가 아니라 T_ORD 가 ORD_SEQ 순으로 쌓여 있어서다 (체크리스트 E)
☐ [남의 눈] rev00 이 5만 번째 행을 넣는 순간 남에게 보이는 정산 = 50000 건 ← 09:03 조민우
☐ rev02 커밋 뒤 남의 눈 = 100000
☐ 13화 「커밋 주기 기준」 #1 과 충돌하나? → 아니다. #1 에서 멈추는 경우이고, '언두가 걱정이면' 에 숫자(0.02 MB)가 붙었다STEP 6 — 05화의 청구서: 월말 정리 (10분)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/06_월말정리_DELETE와_파티션.sql| 정리 방법 | redo | 그 뒤 전체 스캔 | ☐ |
|---|---|---|---|
DELETE 한 달(17,235행) | 4.84MB | 3,602 → 3,602 | ☐ |
SHRINK SPACE(매달 청소) | 17.96MB | 3,489 | ☐ |
DROP PARTITION | 0.01MB | 3,630 → 3,505 | ☐ |
[4] DROP 뒤 GLOBAL 인덱스 | UNUSABLE · 이력 조회 ORD_SEQ = 4242 → 전체 스캔 3,505 | ☐ | |
[4] UPDATE GLOBAL INDEXES로 | VALID · ORPHANED_ENTRIES YES · 같은 조회 3 | ☐ |
☐ 05화의 3,602 가 같은 생성식에서 그대로 나왔나?
☐ 한 달 DELETE 는 0.06초다. 그런데 왜 '청구서' 인가? → 시간이 아니라 읽는 양과 매달의 청소
☐ 파티션으로 지은 쪽은 세그먼트 30개 · 240 MB — 파티션 없는 쪽 29 MB 와 왜 다른가? (2_파고들기 §8.5)STEP 7 — 칸막이를 몇 개 여는가: Pruning (10분)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/07_Pruning_한빛.sql채점표: 돌리기 전에 각 줄의 Pstart / Pstop을 먼저 적어 보자.
| 조건 | 내 예상 | 실측 Pstart/Pstop | Buffers |
|---|---|---|---|
[A] TO_CHAR(ORD_DT,'YYYYMMDD') BETWEEN … (rev00) | 1 / 37 (ALL) | 5,868 | |
[B] ORD_DT >= … AND < … (rev01) | 30 / 30 (SINGLE) | 161 | |
| [C] 바인드 변수 | KEY / KEY | 161 | |
[D] TRUNC(ORD_DT,'MM') = … | 1 / 37 | 5,868 | |
| [E] 6 ~ 8월 | 30 / 32 (ITERATOR) | 493 | |
| [F] 달력 테이블이 고른 달 | KEY / KEY | 163 · 165 | |
[G] LOCAL Prefixed (ORD_DT, ORD_AMT) | 옵티마이저는 6월 칸 FULL | 161(강제하면 89) | |
(기준) 원본 T_ORD_BIG · Q1 rev02 | — | 91 | |
[H] LOCAL Nonprefixed (CUS_ID) · 날짜 없이 | 1 / 37 · 인덱스 Starts 36 | 572 | |
| [H] 같은 인덱스 · 6월과 함께 | 30 / 30 | 16 | |
[H] GLOBAL (CUS_ID) | ROWID | 504 |
☐ 칸막이도 '가공하지 않은 키' 만 알아듣는다 — TRUNC(…,'MM') 처럼 월 단위 가공도 안 된다
☐ 파티션 한 칸 FULL(161) 이 커버링 인덱스(91)보다 많다 — 파티션은 무엇을 빠르게 해 주나?STEP 8 — 버거원: 이미 파티션을 쓰는 곳 (15분)
bash
docker exec -i ora-burger sqlplus -s dev/dev@localhost/FREEPDB1 < 실습/09_버거원_파티션.sql| 확인 | 기대 | ☐ |
|---|---|---|
| [1] 크기 | ORD 1 · 176MB / ORD_LIST 24 · 384MB / ORD_LIST_HASH 192 · 3,072MB | ☐ |
| [1] 2012년 6월 칸 | ORD_LIST full 971 · 빈 1,006 / ORD_LIST_HASH 서브마다 full 약 121 · 빈 1,006 | ☐ |
[2a] ORD_YM = '201206' | LIST SINGLE · 1,980 | ☐ |
[2b] SUBSTR(ORD_DT,1,6) = '201206' | LIST ALL 1~25 · 47,460 | ☐ |
[2c] ORD_DT LIKE '201206%' | LIST ALL + 로컬 인덱스 24번 · 41,897 | ☐ |
[2e] ORD_LIST_HASH 한 달 | SINGLE + HASH ALL 1~8 · 9,039 | ☐ |
[2f] (기준) 파티션 없는 ORD | 1,089 | ☐ |
[3] PK (ORD_NO, ORD_YM)에 번호만 | RANGE ALL · 50 / 번호 + 월 3 / ORD 4 | ☐ |
| [4] 이관 스크립트가 남긴 병렬도 | DEGREE 1(만들 때 PARALLEL 8 → 바로 NOPARALLEL) · Parallel execution FALSE | ☐ |
★★ 3GB의 정체 — 지우고, 다시 지어 본다
⚠️
10은ORD_ITEM_LIST_HASH(약 3GB)를 지운다. 되돌리려면 L2 카드 §4를 다시 돌린다(약 40분). 이유: 같은 설계를 다른 방법으로 다시 지어 비교하려면 약 1.9GB가 필요한데, 버거원 DB는 12GB 한도까지 1GB도 채 남아 있지 않다(실측: PDB 세그먼트 합계 11,321MB, 지운 뒤 8,082MB).ORD_ITEM_LIST_HASH는 [1]에서 크기를 이미 쟀고ORD_LIST_HASH와 같은 병을 앓고 있다. 버거원은 14화 이후로는 쓰지 않는다.
bash
docker exec -i ora-burger sqlplus -s dev/dev@localhost/FREEPDB1 < 실습/10_버거원_공간회수와_재설계.sql| 같은 100만 행 · LIST 25 × HASH 8 | 할당 | 고수위선 아래 | 6월 전체 스캔 | ☐ |
|---|---|---|---|---|
원래(빈 테이블 + APPEND) | 3,072MB | 223,410 | 9,039 | ☐ |
CTAS | 1,536MB | 26,792 | 982 | ☐ |
CTAS + INITIAL 64K | 379MB | 25,640 | 998 | ☐ |
| DEV 세그먼트 합계 | 지우기 전 10,794MB → 지운 뒤 7,551MB | ☐ |
☐ 3GB 의 절반은 '자리'(192칸 × 8 MB), 절반은 'APPEND 가 비워 둔 빈 블록' — 읽기를 9배로 만든 건 어느 쪽?
☐ 해시 8조각은 무엇을 위해 나눴나? 버거원 화면 중 ORD_NO + ORD_YM 으로 찾는 것이 있나? — 근거가 없으면 설계가 아니다버거원 작업이 끝났으면
docker stop ora-burger로 멈춘다(메모리 1~1.5GB 회수).
STEP 9 — 병렬 판독 과제 (Free 에서는 못 잰다)
① '안 됐다'는 증거 다섯 — 이 DB에서 잰다
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/08_병렬_강등.sql| 증거 | 기대 | ☐ |
|---|---|---|
힌트 없음 vs PARALLEL(o 8)의 Plan hash value | 같다(4189269332) · PX 줄 없음 | ☐ |
Hint Report | U - Unused PARALLEL(o 8) | ☐ |
V$PQ_TQSTAT · V$PX_SESSION | 0 · 0 | ☐ |
queries parallelized · downgraded to serial | 0 · 0(강등 기록조차 없다) | ☐ |
FORCE PARALLEL QUERY · ENABLE PARALLEL DML 뒤 PQ_STATUS | DISABLED 그대로 · 에러 없음 | ☐ |
② 판독 — EE의 병렬 계획 (2_파고들기 §9.3의 계획을 보고 답한다)
① QC 는 몇 번 줄인가?
② Granule 은 블록 범위인가, 파티션인가? 어느 줄에서 아나?
③ TQ 는 몇 개이고, 각각 무엇을 어디로 보내나?
④ 두 테이블을 HASH 로 재분배했다. PQ_DISTRIBUTE(o BROADCAST NONE) 이면 9 · 13번 줄은?
⑤ DOP 4 를 다 못 얻으면 무슨 일이 일어나고, 어디에 기록되나?▶ 적고 나서 펼치기
① 1번 PX COORDINATOR다(2번 PX SEND QC로 결과를 받는다). ② 블록 범위다. 10번과 14번이 PX BLOCK ITERATOR다. 파티션 단위라면 PX PARTITION RANGE ALL 같은 줄이 그 자리에 온다. ③ 4개다. :TQ10000(M_CUS → 조인 서버, HASH), :TQ10001(T_ORD_BIG → 조인 서버, HASH), :TQ10002(1차 집계 → 2차 집계 서버, HASH), :TQ10003(최종 집계 → QC, P->S). ④ 작은 쪽(M_CUS)을 모든 소비자에게 통째로 보낸다. PX SEND BROADCAST가 되고 PQ Distrib은 BROADCAST다. 큰 쪽(T_ORD_BIG)은 재분배 없이(NONE) 읽은 서버가 그대로 조인하므로 :TQ10001이 사라진다. ⑤ 서버를 덜 받은 만큼 DOP를 낮춰 돈다(또는 직렬로 돈다). V$SYSSTAT의 'Parallel operations downgraded …' 값이 올라간다. PARALLEL_MIN_PERCENT를 걸어 두면 DOP를 낮추는 대신 ORA-12827로 실패한다. Free에서는 ⑤의 통계조차 오르지 않는다(①표: downgraded 0). 계획을 세울 때부터 직렬이기 때문이다.
대규모 병렬 실측(서버 수, TQ별 행 분포, 다운그레이드)은 별권에서 EE로 한다.
STEP 10 — T3 배치 SLA 리포트 1호 + T2 개정 ★ 오늘의 산출물
양식: T3_배치_SLA_리포트.md (이 화 제정) · T2_인덱스힌트_변경승인서.md (이 화 개정: 「적재 비용」 칸) · 정합성 회귀 체크리스트 · 커밋 주기 기준
┌────────────────────────────────────────────────────────────────────────────────┐
│ T3 배치 SLA 리포트 — 1호 주간 야간 체인 (일요일 밤) │
├────────────────────────────────────────────────────────────────────────────────┤
│ 1. 배치 · 창 : 트리거 일 22:00 · 마감 월 04:00 · 6/2 실제 끝 월 09:12 (초과 5h12m) │
│ 2. 환경 : 연습 DB 복사본 · 23ai Free · NOARCHIVELOG · 리두 10MB×2 · 단독 실행 │
│ 3. 단계 분해 : (아래 표) │
│ 4. 처리량 : 재적재 100만 행 — 지금 0.329 ms/행 · 바꾼 뒤 0.005 ms/행 │
│ redo 약 961 B/행 → 약 0.6 B/행 │
│ 5. 자원 : redo 916.6 MB → 0.6 MB · undo 344.4 MB → 0.09 MB │
│ 6. 재기동 · 멱등 : 재적재 = TRUNCATE 부터 (멱등) · P1 rev02 = TRUNCATE + 한 문장 (멱등) │
│ rev00 은 중간 실패 시 '반쯤 적재' (13화 E·H) │
│ 7. 동시 실행 영향: 적재 동안 TM LMODE 6 (남의 INSERT 불가) · 인덱스 꺼진 동안 │
│ Q1 91 → 5,822 (조용히) · P1 적재 동안 정산 0건 │
│ 8. 추세 : 측정 불가 — 운영 로그에 단계별 시각 없음 (재발방지 1) │
│ 9. 개선 이력 : P1 rev00 → rev02 · 재적재 방식 (일반 → APPEND + 끄고 켜기) │
│10. 정합성 : 체크리스트 A·B·E·H (STEP 5) │
│11. 커밋 주기 : 「커밋 주기 기준」 #1 (집합 연산 → 커밋 한 번) · 언두 0.02 MB │
│12. 재발방지 : ① 단계별 시각 기록 ② 통계 수집 단계 유지(히스토그램) │
│ ③ PARALLEL(8) 제거 후 운영 재측정 ④ 파티션 예산(근거 STEP 6) │
│ ⑤ 파티션 도입 시 INITIAL 명시 (STEP 4 [4]) │
└────────────────────────────────────────────────────────────────────────────────┘3번 「단계 분해」 표는 연습 DB 실측으로 채우고, 칸마다 출처 스크립트를 적는다.
| 단계 | 지금 | 바꾼 뒤 | 출처 |
|---|---|---|---|
| ① 재적재 | 일반 INSERT · 인덱스 3개 켠 채 329초 · redo 916.6MB | APPEND + 끄고 켜기 5.0초 · redo 0.6MB | 02 [A] · 03 [C] |
| ① 통계 수집 | 1.06초 | 1.06초(유지: 온라인 통계는 히스토그램을 안 모은다) | 02 [A] · 03 [B] |
| ② 월말 이력 정리 | DELETE 0.06초 · 전체 스캔 3,602 그대로 | (파티션 승인 시) DROP PARTITION 0.04초 · redo 0.01MB | 06 |
| ③ 월마감 집계 | PARALLEL(8) · 0.05초 · Hint Unused | 힌트 제거 · 운영에서 재측정 | 08 |
| ④ P1 | rev00 0.89초 · redo 28.66MB | rev02 0.11초 · redo 0.08MB | 05 |
| 합계 | 약 331초 | 약 6.3초 |
⚠️ 연습 DB의 초를 운영의 초로 옮기지 않는다. 운영 로그에 단계별 시각이 없으므로 이 표로 알 수 있는 것은 '어느 단계가 몸통인가'와 '방식을 바꾸면 무엇이 몇 배 줄어드는가'까지다. 운영의 초는 재발방지 1번을 적용한 뒤에 잰다.
T2-002 마무리 — IX_ORD_BIG_DT 폐기 (04화 승인 대기)
개정된 T2의 3-2 「적재 비용」 칸을 채운다(STEP 2의 마지막 줄).
적재 방식 : 일반 INSERT (지금) / APPEND + 끄고 켜기 (바꾼 뒤)
5만 건 : 인덱스 3개 redo 38.55 MB → (ORD_DT) 만 뺀 2개 redo 26.85 MB (−11.70 MB · 30%)
100만 건 : 일반 INSERT 면 행마다 청구 / 끄고 켜기면 재생성 시간만 (STEP 3 [C] 재생성 3개 4.74초 중 1개 몫)
조회 영향 : Q1 rev02 는 (ORD_DT, ORD_AMT) 를 쓴다 → 91 유지 (STEP 7 기준)
선결 조건 : T_ORD_BIG 참조 SQL 전수 · DBA_INDEX_USAGE 관찰 — 04화 그대로적재 방식을 먼저 적는 이유: 같은 인덱스라도 일반 INSERT로 켠 채 넣으면 '행마다' 비용이 청구되고(5만 건에 12MB), 끄고 켜기로 넣으면 '재생성 몇 초'로 바뀐다. 이 칸이 비어 있으면 인덱스 하나의 값을 정할 수 없다. T2를 고친 것도 그래서다.
STEP 11 — 서술 답안 (400~600자)
박팀장에게 보내는 회신. "배치가 왜 아침을 넘겼고, 다시는 안 넘기려면 무엇이 필요한가."
▶ 선배의 첨삭 포인트 (쓰고 나서)
① "월말 정리 때문"으로 끝내지 않는다. 이번 주에 달라진 것은 월말 정리였지만, 체인의 99%는 재적재였다(연습 DB에서 재적재 329초, 월말 정리 0.06초). 넘치게 한 것과 채운 것을 나눠서 쓴다.
② 인덱스를 "지우겠다"고 쓰지 않는다. 인덱스는 낮에 Q1의 91과 Q4 포스트잇을 번다. "적재하는 동안만 끄고 다시 짓는다"고 쓰고, 그 몇 초 동안 Q1이 5,822로 떨어진다는 대가도 함께 쓴다.
③ 연습 DB의 초를 약속하지 않는다. "329초 → 5초"는 연습 DB에서의 비율이다. 운영의 초는 단계별 시각을 남긴 뒤 다음 주에 보고한다.
④ 무기 둘을 함께 쓸 때의 함정을 미리 쓴다. 파티션 예산을 올리면서 "INITIAL을 명시하지 않으면 한 달 조회가 7배"라는 점도 같이 적는다. 예산이 나온 다음에 알면 늦다.
STEP 12 — 파인만 자가설명
1단 백지 설명 → 2단 막힘 태그(최소 1개) → 3단 원천 복귀 → 4단 재설명.
| 태그 | 전형적인 문장 |
|---|---|
#인과 | "인덱스를 켠 채 넣으면 왜 리두 로그 교체를 기다리게 되는지 설명하다 막혔다" → §2.3 |
#용어 | "Pstart/Pstop의 KEY와 숫자의 차이를 말로 못 하겠다" → §7.1 |
#수치 | "파티션 6월 칸이 왜 161이 아니라 1,169인지 블록으로 못 나눠 보겠다" → §8.5 |
4단 되묻기 3문 (오늘 버전)
- 조건 반전: "재적재 대상 테이블에 FK가 있으면 오늘의 무기는 어떻게 되나?"
- 반례 요구: "파티션으로 바꿨는데 조회가 더 비싸지는 경우 둘은?"(힌트: 161 vs 91 · 1,169)
- 측정 지목: "'PARALLEL 8이 효과가 있었다'를 증명하려면 무엇을 재야 하나? 이 DB에서 그게 되나?"
태그는 _막힘로그.md로 옮긴다.
정리 — 한빛유통을 13화 종료 상태로
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/99_정리.sql기대: c14_left 0 · 1000000 · 3 · FREQUENCY · 100000 · 0 · VALID · UNDOTBS1 · 원본 T_ORD_BIG 5,923블록 · 인덱스 2,653 · 3,206 · 3,344
완료 체크리스트
- [ ] STEP 0:
P1_rev00의 13화 메모를 읽었고, 버거원 스테이징 두 개를 지웠다 - [ ] STEP 1: 보기 전에 예측 6개를 적었다
- [ ] STEP 2: 인덱스 0 → 3개에서 redo 2.44 → 38.55MB를 확인했다
- [ ] STEP 3: 329초 → 5초를 확인했고, [B] 직후
ORD_ST히스토그램이 사라진 것도 봤다 - [ ] STEP 4:
LMODE 6· 빈 칸 무시 · 파티션 6월 161 → 1,169를 봤다 - [ ] STEP 5: P1 rev02를 돌리고 정합성 다섯 항목을 통과시켰다
- [ ] STEP 6:
DELETE는 3,602 그대로이고DROP PARTITION은 redo 0.01MB인 것, GLOBAL 인덱스가 UNUSABLE이 되는 것을 봤다 - [ ] STEP 7: rev00
1/37, rev0130/30을 먼저 예측해 적고 대조했다 - [ ] STEP 8: 버거원 3GB의 정체를 쟀다(선택)
- [ ] STEP 9:
U - Unused를 확인하고 판독 과제 5문에 답했다 - [ ] STEP 10: T3 1호를 쓰면서 「동시 실행 영향」과 「추세(측정 불가)」 칸을 채웠고, T2-002 「적재 비용」 칸도 채웠다
- [ ] STEP 12: 막힘 태그 최소 1개를
_막힘로그.md에 옮겼다 - [ ] 정리:
99로 13화 종료 상태를 확인했다
완결성 게이트: STEP 10의 「동시 실행 영향」, STEP 5의 정합성, STEP 3의 히스토그램 확인이 비어 있으면 이 화는 미완성이다.
다음 화로 넘기는 상태
| 무엇 | 값 |
|---|---|
ora-hanorder | 변화 없음. 13화 종료 상태 그대로(T_ORD_BIG 인덱스 3 · ORD_ST 히스토그램 · T_ORD_STLM 10만 · PKG_ORD_STLM 원본 · C14/P14 0) |
ora-burger | ORD_ITEM_LIST_HASH 삭제됨(STEP 8을 했다면) · ORD_TEMP/ORD_TEMP2 삭제됨. 14화가 마지막 무대 |
P1 현재 리비전 | rev02(APPEND · 커밋 한 번). 리비전 파일로만 남는다. 연습 DB의 PKG_ORD_STLM은 원본 그대로 |
| 새 양식 · 개정 | T3 배치 SLA 리포트(제정 · 1호) · T2 개정(「적재 비용」 칸 · T2-002) |
| 열어 둔 것 | 커서 루프의 내부 비용 → 15화 · 운영 체인의 시간 → 16·17화 |
다음 → 15화 「SQL은 다 고쳤는데 왜 느린가」: 오늘 P1은 한 문장이 됐다. 다음 주에는 한 문장으로 바꿀 수 없는 배치가 온다.