테마
18화 · 실습과 기록
여기서 하는 일: 3월의 원장에 2열을 붙이고, 계획을 베이스라인으로 지키고, 튜닝 어드바이저의 권고를 재서 기각한다. 팩을 꺼 보고, 후배의 리포트를 숫자로 다시 쓰고, 배치와 화면을 같은 시간에 돌려 본다. 옛 판과 지금 판을 한 리포트에 놓고, 18주의 리포트로 표준을 만든다. 그리고 처음 보는 고객사를 혼자 진단한다.
1_사건.md를 읽은 것은 "선배가 하는 걸 지켜본 것"이고, 이 문서를 끝내야 직접 해 본 것이 된다. 개념이 막히면2_파고들기.md를 펼친다.시간이 없는 주에는 STEP 0 → 2 → 6 → 10 → 12만 한다(원장 2열, 후배 리포트, 표준, 졸업 과제로 약 3시간). 부하를 걸어야 하는 STEP 7(21분)과 STEP 8(21분)은 띄워 두고, 기다리는 동안 STEP 9와 10을 한다.
이 화의 실습 지도
| 스크립트 | 무엇 | 계정 | 소요 |
|---|---|---|---|
실습/00_준비와_점검.sql | 17화 종료 상태 · 원본 지문 · 오늘 쓸 것(C18 · S18 · T18 · W18)이 아직 없나 · 설정 · 권한 | sqlp | 수 초 |
실습/01_튜닝권한_SYSDBA.sql | sqlp에 ADVISOR · ADMINISTER SQL MANAGEMENT OBJECT (99가 거둔다) | SYS | 1초 |
★ 실습/02_W01_재측정.sql | 6조각: rev00 그대로 오늘 · 최신판 오늘 · P1 · N1은 복사본 | sqlp | 약 10초 |
★ 실습/03_SPM_계획을_지킨다.sql | 베이스라인 · 대기 줄 · 진화 · 그 일요일 밤(10화) · FIXED · ENABLED · 자동 캡처 (복사본) | sqlp | 약 10초 |
실습/04_SPM_자동기능_SYSDBA.sql | Free의 자동 진화 · 자동 튜닝: 켜 보기 한 번 · 실행 기록 | SYS | 1초 |
★ 실습/05_튜닝_어드바이저.sql | 17화 ADDM Top SQL 셋 + 강도현 원본 셋에 STA · Q2 rev00 프로파일 받기 · 정체 · 지우기 | sqlp | 약 10초 |
★ 실습/06_팩_확인란_SYSDBA.sql | 팩 설정 네 판 (메모리만): 과제 · 베이스라인 · 프로파일 · 되돌린다 | SYS | 약 5초 |
실습/07a_리소스플랜_만들기_SYSDBA.sql | 그룹 둘 · 플랜 둘 · 매핑 · 배치 기록 테이블 | SYS | 수 초 |
실습/07_격리_세_판.sh + 07_배치_일꾼.sql · 07c_격리_결과.sql | 온라인만 · +배치 · 90/10 플랜 · 상한 플랜: 5분씩 네 판 | Git Bash | 21분 |
실습/08_옛판_지금판.sh + 08_구간_스냅샷_SYSDBA.sql · 08b_awrddrpt_SYSDBA.sql · 08c_두_구간_요약_SYSDBA.sql | 나쁜 판 10분 · 지금 판 10분 · 비교 리포트 · 요약 | Git Bash | 21분 |
★ 실습/09_Q2_보류안_T1.sql | Q2 rev01 vs rev02: 계획 · CPU 10회 × 3 · 정합성 · 경계 (복사본) | sqlp | 약 6초 |
실습/10_IX_ORD_BIG_DT_관찰.sql | T2-002: 인덱스 사용 · AWR 계획 · INVISIBLE로 한 걸음 → 되돌림 | sqlp | 약 2초 |
실습/11_결과캐시_점검.sql | 15b화가 넘긴 셋: RESULT_CACHE 전수 · 칸 수 · 고객 수 | sqlp | 1초 |
실습/99_정리_SYSDBA.sql | 베이스라인 · 프로파일 · 과제 · 플랜 · 시험 객체 · 권한 → 17화 종료 상태 확인 | SYS | 수 초 |
리비전/Q2_rev02.sql | 18화의 유일한 리비전 | — | — |
졸업 과제 실습/졸업과제/ | ora-newshop: E00 권한 · E1 ~ E4 현업 원본 · E01 원본 재기 · 🔒 E90 · E91 모범 답안 · E99 봉인 복구 | SYS + ecommerce | 반나절 |
SYS로 돌리는 스크립트: Git Bash에서는
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < …로 돌린다(앞의 설정을 빼면/가 경로로 바뀐다). PowerShell에서는Get-Content … -Encoding UTF8 | docker exec -i ora-hanorder sqlplus -s / as sysdba로 돌리고, 그 전에$OutputEncoding = New-Object System.Text.UTF8Encoding $false한 줄을 실행해 둔다. 원본 테이블에는 한 줄도 쓰지 않는다.T_ORD_BIG·T_ORD_STLM·M_CUS를 바꾸는 시험은 전부 복사본(*_C18)에서 한다. 부하 발생기는*_LD에만 쓴다. 인스턴스 설정은 세 가지를 바꿨다가 되돌린다.06의CONTROL_MANAGEMENT_PACK_ACCESS(메모리만),07의 PDBRESOURCE_MANAGER_PLAN(메모리만, 빈 값으로 끄지 않는다, §7.4),10의IX_ORD_BIG_DTINVISIBLE(끝에서 VISIBLE)이다.99가 한 번 더 확인한다. 스크립트는 두 번 돌려도 같은 결과가 나온다(이 화를 쓰며 전부 두 번씩 돌렸다).
STEP 0 — 전제 확인 · 3월의 원장을 다시 연다 (5분) ★ 매주 처음 하는 일
bash
docker start ora-hanorder
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/00_준비와_점검.sql
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/01_튜닝권한_SYSDBA.sql| 확인 | 기대 | ☐ |
|---|---|---|
| [1] big_rows · big_idx · hist · stlm_rows · last_no · pkg · driver · driver_alive · ld_seq_cache | 1000000 · 3 · FREQUENCY · 100000 · 0 · VALID · VALID · 0 · 20 | ☐ |
| 인덱스 셋 | IX_ORD_BIG_DT · _DT_AMT · _DT_SEQ 전부 VALID · VISIBLE · CF 1,000,000 | ☐ |
| [2] 원본 지문 | T_ORD_BIG 1000000 / 255,398,623,700 · T_ORD_STLM 100000 / 25,519,833,100 · T_ORD_DET 30,272,557,700 | ☐ |
| [3] 오늘 쓸 것 | baselines · profiles · my_tasks · c18_objects · c18_rsrc_plans · c18_groups: 전부 0 | ☐ |
| [4] 설정 | control_management_pack_access DIAGNOSTIC+TUNING · cpu_count 2 · job_queue_processes 1 · resource_manager_plan DEFAULT_PLAN · AUTO_SPM_EVOLVE_TASK OFF | ☐ |
| [5] 권한 (01 뒤 다시 접속) | ADMINISTER SQL MANAGEMENT OBJECT · ADVISOR · ALTER SESSION · SELECT ANY DICTIONARY | ☐ |
3월의 원장을 연다. W01_베이스라인_원장.md에서 01화 표 위의 "인덱스 상태 · T_ORD_BIG 인덱스 0개 ← 이 원장의 전제"에 동그라미를 친다. 01화 표는 고치지 않는다. 2열은 그 아래 18화 절에 붙인다(STEP 2).
STEP 1 — 예측을 봉인한다 (5분)
Q1. 3월의 Q4 (rev00 · 5,826 블록) 를 SQL 한 글자도 안 고치고 오늘 다시 돌리면? ______
Q2. 인덱스 없는 표에서 FULL 계획을 베이스라인으로 올린 뒤 인덱스를 만들면, 다음 실행은? FULL / 인덱스
Q3. Q2 rev00 (USE_NL 이 박힌 원본) 에 SQL Tuning Advisor 를 돌리면 무엇을 권할까? ______
Q4. 튜닝 팩을 끄면(CONTROL_MANAGEMENT_PACK_ACCESS = DIAGNOSTIC) 이미 받아 둔 SQL 프로파일은? 에러 / 무시 / 그대로
Q5. Q2 rev02 (먼저 묶기) 는 rev01 보다 Buffers 가 몇 블록 적을까? CPU 는? ______ / ______
Q6. 온라인 90 · 배치 10 으로 리소스 플랜을 켜면, 배치 때문에 절반이 된 화면 처리량은 얼마나 돌아올까? ______ %▶ 적고 나서 펼치기
- Q1: 1,007(Q4c는 3,365). 09화의
IX_ORD_BIG_DT_SEQ를 rev00도 쓴다. SQL이 아니라 바닥이 바뀌었다. - Q2: FULL(5,821). 인덱스 계획은 대기 줄(ACCEPTED NO)로 간다. 진화(시험 운행)로 63.7배를 확인한 뒤에야 91이 된다.
- Q3: SQL 프로파일 · 예상 이득 99.42%(1,026,849 → 5,868). 그 정체는
IGNORE_OPTIM_EMBEDDED_HINTS, 곧 박힌 힌트를 무시하라는 지시다. - Q4: 에러 없이 무시된다. 1026K로 돌아간다. 막히는 것은 새 과제를 만드는 일(
ORA-13717)뿐이다. 베이스라인은 쓰인다(SPM은 팩 기능이 아니다). - Q5: 1블록(5,869 → 5,868). CPU 10회 합은 115 → 33cs. 해시 조인에 들어가는 행이 100만 → 2,000으로 준다.
- Q6: 이 Free에서는 16 ~ 19%(판 B 20,497 → 판 C 23,754 /분 · 배치 없을 때 41,741 · 두 번 돌려 16% · 19%). 비율도 상한도 약속대로 나누지 않았다. 원인은 밝히지 못했다.
STEP 2 — W01 원장에 2열을 붙인다 (15분) ★
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/02_W01_재측정.sql| [3] 원장 줄 | 기대 (Buffers · 계획 해시) | ☐ |
|---|---|---|
| rev00 Q1 · Q2 · Q3 | 5,826 · 914281838 / 1,026,849 · 2289018667 / 840 · 119122671: 01화와 같은 계획 | ☐ |
| rev00 Q4 · Q4c | 1,007 · 3040227993 / 3,365 · 97409517: 01화와 다른 계획 | ☐ |
| now Q1 · Q2 · Q3 · Q4 | 91 · 92368507 / 5,868 · 2292855265 / 839 · 2231864090 / 14 · 3610158121 | ☐ |
| rev00 Q1 자식 1 (두 번째 실행부터) | 같은 계획 · 같은 Buffers: statistics feedback | ☐ |
| [4] P1 rev00 → rev02 (복사본 · 둘째 판) | 약 1초 → 0.1초 · 논리 읽기 약 110,000 → 약 2,900 · redo 28.6 → 0.1MB · 커밋 101 → 2 | ☐ |
| [4] N1 rev00 → rev01 (100회) | 56,800 → 0 ~ 23 | ☐ |
| P1 결과 = 원본 | 100000 · 25,519,833,100 · C18_LEFT 0 | ☐ |
| [5] Q4 rev03 100페이지 = rev00 100페이지 | 10 = 10 · 2,627,200 = 2,627,200 · MINUS 0 / 0 | ☐ |
원장에 적는다. W01_베이스라인_원장.md의 18화 절에 측정 조건(01화와 다른 점 둘), 6조각 3열(01화 · rev00 오늘 · 최신판 오늘), 조각마다 '그때는 왜 옳았고 지금은 왜 다른가' 한 줄(2_파고들기.md §1.4), 다음 재측정 조건을 적는다. 교재의 2열은 그 파일에 이미 있다. 자기 숫자를 먼저 적은 뒤에 대조한다.
'빨라졌다'고 쓰기 전에 한 번 더 본다. Q4 rev00의 1,007은 rev00이 빨라진 것이 아니다. P1 rev00의 2,480 → 1,000ms는 같은 코드다. 3월의 원장에는 '설치 후 두 번째 실행'이라고만 적혀 있어서 초의 차이를 가를 수 없다. 그래서 오늘은 일의 양(논리 읽기 · redo · 커밋)을 같이 적는다.
STEP 3 — 계획을 지킨다 · SQL Plan Management (15분) ★
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/03_SPM_계획을_지킨다.sql > spm.txt
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/04_SPM_자동기능_SYSDBA.sql| 장면 | 확인 | 기대 | ☐ |
|---|---|---|---|
| A [1] | FULL을 베이스라인으로 | MANUAL-LOAD-FROM-CURSOR-CACHE · YES · Buffers 5,821 | ☐ |
| A [2] | 인덱스를 만들어도 | FULL 그대로 · SQL plan baseline … used · 대기 줄 AUTO-CAPTURE · ACCEPTED NO | ☐ |
| A [3] | 진화 | passed the benefit criterion … 63.7 times better → INDEX RANGE SCAN 91 | ☐ |
| B [5] | 빈 테이블 통계 (NUM_ROWS 0) + 베이스라인 | HASH 5,868 · E-Rows 1 · 대기 줄에 NL | ☐ |
| B [6] | 내 세션만 베이스라인 끔 | NESTED LOOPS 1026K · PK_M_CUS Starts 1000K | ☐ |
| B [7] | NL을 진화 | failed the benefit criterion … 0.33 times worse | ☐ |
| B [8] | ENABLED = NO | 베이스라인 없이 → NL 1026K | ☐ |
| C [9] | 세션 자동 캡처 | 첫 실행 뒤 0 · 두 번째 뒤 1 | ☐ |
04 | 루트에서 ON | ORA-38153 · 자동 과제 실행 0 · ACCEPT_SQL_PROFILES FALSE | ☐ |
⚠️
DBMS_SPM.CONFIGURE('AUTO_SPM_EVOLVE_TASK', 'OFF')는 부르지 않아야 한다. 값은 그대로인데LAST_MODIFIED가 찍혀 남는다(2_파고들기.md §3). 정기 점검(T8)에 더할 줄도 적어 둔다. 대기 줄(ACCEPTED = NO)이 있는 베이스라인 → 진화를 사람이 돌린다 (Free는 자동이 없다).
STEP 4 — 튜닝 어드바이저의 권고를 잰다 (15분) ★
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/05_튜닝_어드바이저.sql > sta.txt| 과제 | 기대 | ☐ |
|---|---|---|
| [1] 17화 ADDM의 SQL_ID 셋이 커서 캐시에 | 8ac5jyu193w2j · d8v5hzdaa2zt4 · 9jg2gb66dwsvu | ☐ |
| 요약 표 | ADDM_Q1 · Q2 · Q3 권고 없음 / R0_Q1 RESTRUCTURE SQL 2 / R0_Q2 SQL PROFILE 1 · 99.42 / R0_Q3 없음 | ☐ |
| T18_R0_Q2 리포트 | Buffer Gets 1026849 → 5868 · 힌트 보고서 LEADING(o c) / rejected by IGNORE_OPTIM_EMBEDDED_HINTS | ☐ |
| [4] 받은 뒤 | HASH JOIN · 2292855265 · 5,869 · SQL profile PROF_T18_Q2 used | ☐ |
| 프로파일의 힌트 | OPTIMIZER_FEATURES_ENABLE(default) · IGNORE_OPTIM_EMBEDDED_HINTS: 두 줄 | ☐ |
| 띄어쓰기 · 대소문자만 다른 문장 | 프로파일이 붙는다 | ☐ |
| 지운 뒤 | NL · 2289018667 · 1026K · PROFILES_LEFT 0 | ☐ |
결론 칸은 일단 비워 둔다. STEP 5까지 잰 뒤에 T1 13호(STEP 11)에 채택이나 기각을 적는다. Q3 rev00(금액 ×50)에 권고가 없다는 줄에는 동그라미를 친다. 도구는 빠른지를 보고, 맞는지는 보지 않는다.
STEP 5 — 팩 라이선스 확인란 (10분) ★
bash
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/06_팩_확인란_SYSDBA.sql| 판 | (1) 과제 | (2) 베이스라인 | (3) 프로파일 | (4) 베이스라인 사용 | ☐ |
|---|---|---|---|---|---|
| DIAGNOSTIC+TUNING | OK | OK | 2292855265 · 5,869 · profile used | baseline used | ☐ |
| DIAGNOSTIC | ORA-13717 | OK | 2289018667 · 1026K (Note에 프로파일 없음) | baseline used | ☐ |
| NONE | ORA-13717 | OK | 1026K | baseline used | ☐ |
| DIAGNOSTIC+TUNING | OK | OK | 5,869 | baseline used | ☐ |
| [9] 되돌린 값 | 메모리 · spfile 둘 다 DIAGNOSTIC+TUNING · 프로파일 0 | ☐ |
마지막 줄이
DIAGNOSTIC+TUNING이 아니면 바로99를 돌린다. 메모리에서만 바꿨으므로 컨테이너를 다시 띄워도 spfile 값으로 돌아온다. T6 §4 팩 라이선스 확인란을 이 표로 채운다.
STEP 6 — 후배의 리포트를 다시 쓴다 · Q2 rev02 (40분) ★
정하은 씨의 초안(그대로 옮긴다):
[튜닝 결과 보고] 등급별 매출 집계 화면
- 개선 내용 : 주문을 고객별로 먼저 집계한 뒤 고객 등급과 조인하도록 변경
- 결과 : 약 3배 빨라졌습니다 (개발 DB 에서 테스트 완료)
- 첨부 : 변경 SQL 1건먼저 묻고, 답은 하지 않는다. 이 초안에 빠진 것을 01화에서 선배가 던진 세 질문(무엇을, 어떤 조건에서, 어떤 단위로)과 T6 마지막 조항(규모, 근거 수치)에 비추어 적는다. 다섯 줄 이상이면 된다.
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/09_Q2_보류안_T1.sql| 확인 | 기대 | ☐ |
|---|---|---|
| [1] 계획 | rev01 HASH JOIN 1000K · 5,869 · 2292855265 / rev02 VIEW 2,000 → HASH JOIN 2,000 · 5,868 · 653275336 | ☐ |
| [2] CPU 10회 × 3 | rev01 115 ~ 121cs / rev02 32 ~ 35cs · Buffers/run 5,868 둘 다 | ☐ |
| [3] 정합성 | 3 = 3 · 255,398,623,700 = 255,398,623,700 · 0 / 0 | ☐ |
| [4] 경계 ① 지역 코드 두 번 | 두 판 다 ORA-01427 | ☐ |
| [4] 경계 ② 고아 지역 코드 | 두 판 같은 답 · 0 / 0 · C18_LEFT 0 | ☐ |
리포트는 STEP 11의 T1 12호 양식으로 쓴다. 다 쓰면 리비전 파일 리비전/Q2_rev02.sql의 머리말과 대조한다.
STEP 7 — 배치와 화면을 같은 시간에 · 리소스 매니저 (5분 + 21분)
bash
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/07a_리소스플랜_만들기_SYSDBA.sql
cd 실습 && ./07_격리_세_판.sh # 컨테이너 이름이 다르면 C=… · 판 길이는 MIN=… (기본 5분)| 판 | 확인 | 기대 (이 화의 실측 · ± 몇 %) | ☐ |
|---|---|---|---|
| 07a [3] | 플랜 둘 · 매핑 둘 · 아직 DEFAULT_PLAN | C18_DAYTIME_PLAN 90/10 · C18_CAP_PLAN + UTILIZATION_LIMIT 10 · MODULE_NAME LOAD_DRIVER → ONLINE · MONTH_CLOSE → BATCH | ☐ |
| C · D 20초째 | 세션이 제 그룹에 | LOAD_DRIVER → C18_ONLINE_GRP 2 · MONTH_CLOSE → C18_BATCH_GRP 2 | ☐ |
| 07c [1] 온라인 합계 | A · B · C · D 1분당 | 약 42,000 · 20,500 · 24,000 · 23,500 | ☐ |
| 07c [2] 배치 | B · C · D 1분당 판 | 약 990 · 835 · 860 | ☐ |
D 끝 V$RSRC_CONSUMER_GROUP | 배치 그룹 CPU 초 vs 온라인 | 배치가 상한 10%보다 훨씬 많이 쓴다 (실측 265초 vs 342초) | ☐ |
| 끝 | resource_manager_plan = [DEFAULT_PLAN] running = DEFAULT_PLAN | ☐ |
기대와 다르게 나왔다면 그것이 결과다. 이 화에서도 비율과 상한이 약속대로 지켜지지 않았고, 원인을 찾지 못했다(
2_파고들기.md §7). T3 3호의 「동시 실행 영향」 칸에 잰 것과 못 밝힌 것을 나눠 적는다. ⚠️ 중간에 끊었으면 반드시echo "ALTER SESSION SET CONTAINER = FREEPDB1; ALTER SYSTEM SET RESOURCE_MANAGER_PLAN = 'DEFAULT_PLAN' SCOPE = MEMORY;" | MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba로 플랜을 되돌린다. 빈 값('')으로 꺼서는 안 된다.
STEP 8 — 옛 판과 지금 판 · awrddrpt (21분 + 10분)
bash
cd 실습 && ./08_옛판_지금판.sh # 10분 + 10분 + 비교 리포트 → awr_diff_C18.txt · 요약| 확인 | 기대 | ☐ |
|---|---|---|
| 런 둘 | C18-OLD (SPIKE · NL,LIT,LOOP · 10분) · C18-NOW (STEADY · 10분) · 에러 0 · 지문 앞 = 뒤 | ☐ |
| 리포트 머리 | DB time 두 구간 거의 같다 (약 24분 · %Diff 1% 안쪽) | ☐ |
| Workload Comparison | Logical read −90% 이상 · Redo −95% 이상 · Executes −98% | ☐ |
| Top Timed Events | 옛 판 log file switch (checkpoint incomplete) → 지금 판 resmgr:cpu quantum | ☐ |
| 08c [4] 부하 발생기의 셈 | 조각 실행 수 합 약 6배 (실측 58,640 → 363,367) · Q2 294.56 → 40.43ms | ☐ |
| Time Model 맨 위 | sequence load elapsed time: 크게 나와도 결론에 쓰지 않는다 (§8.5의 ASH로 확인) · 이 줄은 런마다 움직인다 (이 화의 두 번: 지금 판 48.3% · 66.4% · 옛 판 0.0% · 3.9%) | ☐ |
리포트는 STEP 11의 T5-003과 T7 3호로 쓴다. T5-003 1번 칸 첫 줄에는 "3월과 7월의 비교가 아니다. 같은 날 연달아 만든 '3월의 나쁜 판 셋'과 '지금 판'의 비교다"라고 적는다.
STEP 9 — 앞 화들이 넘긴 것 (15분)
bash
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/10_IX_ORD_BIG_DT_관찰.sql
docker exec -i ora-hanorder sqlplus -s sqlp/sqlp@localhost/FREEPDB1 < 실습/11_결과캐시_점검.sql| 넘긴 것 | 확인 | 기대 | ☐ |
|---|---|---|---|
T2-002 IX_ORD_BIG_DT (16화) | [1] DBA_INDEX_USAGE | IX_ORD_BIG_DT 수백 회(마지막 사용이 이 화의 부하보다 앞) · _DT_AMT · _DT_SEQ 수백만 회 | ☐ |
| [2] AWR 계획 기록 | IX_ORD_BIG_DT 0 SQL · _DT_AMT 7 ~ 9 · _DT_SEQ 20 (스냅샷 범위는 각자의 AWR에 따라 다르다) | ☐ | |
| [3] 공유 풀 | IX_ORD_BIG_DT 응용 SQL 0 · 동적 샘플링(DS_SVC) 몇 개: 옵티마이저가 03 · 05에서 스스로 던진 표본 쿼리다 (응용 SQL이 아니다) | ☐ | |
| [4] VIS → INV → VIS | 계획 해시가 하나도 안 바뀐다 (Q1 · Q1R0 · Q2 · Q4 · Q4R0 · Q4C · D1) · 끝에 셋 다 VISIBLE | ☐ | |
| 15b화 결과 캐시 | [1] RESULT_CACHE 함수 | 0행 | ☐ |
| [2] 힌트 문장 | 우리 것 0 · 동적 샘플링(OPT_DYN_SAMP) 여럿 | ☐ | |
| [4] 고객 수 vs 칸 | 2,000 vs 1,984 · does not fit → 보정률 함수 재적용 보류 유지 | ☐ | |
| 15b화 체크리스트 줄 | 정합성 체크리스트 경계 목록 | '시각 · 세션' 줄이 있다 | ☐ |
넘겨받은 것 전부의 결론 → 2_파고들기.md §10.
STEP 10 — 사내 튜닝 표준 T6 v1.0 · SQL 코딩 가이드 v1.0 (60분) ★★ 오늘의 산출물
보기 전에 직접 센다. 18주의 리포트(T1 1 ~ 13호 · T2-001/002 · T3 1 ~ 3호 · T4 1 · 2호 · T5-001 ~ 003 · T7 1 ~ 3호 · T8 1호 · 정책서 · 재협상서 · 대장 · 03화 정합성 이상 보고)에서 원인 칸의 말을 한 줄씩 뽑아, 같은 원인끼리 묶는다.
원인 (한 문장) | 리포트 (번호 · 화) | 횟수 | 조문 / 대기
--------------------------------------------+------------------------------------+------+-----------
| | |규칙은 이렇다. 리포트 둘 이상에서 나오면 조문, 하나뿐이면 대기 줄이다. 각주 셋(근거 리포트 · 성능고도화 · 공식문서)은 그 리포트를 쓴 화의 2_파고들기 「원천 좌표」에 있는 것만 옮긴다. 없으면 좌표 미확인으로 적는다.
다 셌으면 교재의 판과 대조한다. T6 사내 튜닝 표준 v1.0(조문 16 · 대기 줄 9 · 체크리스트 26문 · 팩 확인란 · 문서 운용 · 마지막 조항)과 SQL 코딩 가이드 v1.0(조문 8 · 11 · 12 · 13 · 14 · 대기 조문 1 ~ 7 · 9 · 10)이다.
| 대조 | 생각해 볼 것 | ☐ |
|---|---|---|
| 내 조문 수 vs 16 | 더 많다면: 한 번 나온 것을 조문으로 올렸나 · 같은 원인을 둘로 쪼갰나 | ☐ |
| 바인드 조문 (11화) | 숫자가 가장 컸던 화다. 그래도 리포트는 하나라서 대기 줄로 간다 | ☐ |
| 가이드 조문 11 | '결과 캐시'로 보면 1회, '숨은 입력'으로 보면 03화 NLS와 묶여 2회다. 묶는 말에 따라 판정이 갈린다. 어느 쪽이 원인의 말인가 | ☐ |
| 마지막 조항 | 가장 많이 나온 원인(9회). 해설은 "강도현이 하지 않은 단 한 가지"다. 숫자나 규모를 남기지 않은 것이 아니라 함께 적지 않은 것이다(회의록 본문 · 첨부 끝 줄 · 소스 주석이 다 따로였다) | ☐ |
| PR 체크리스트 | 개정계획의 여섯 문항(실행계획 첨부 · access/filter · E/A-Rows · T2 · redo/undo · 롤백)이 다 들어 있나 | ☐ |
STEP 11 — 리포트 (오늘 쓴 여섯)
11-1. T1 단건 SQL 튜닝 리포트 — 12호 (Q2 rev02 · 정하은 초안을 다시 씀)
| # | 칸 | 내용 |
|---|---|---|
| 1 | 대상 | PKG_ORD_STLM.Q2 · rev01 → rev02 · diff: FROM T_ORD_BIG o를 (SELECT CUS_ID, COUNT(*) cnt, SUM(ORD_AMT) amt FROM T_ORD_BIG GROUP BY CUS_ID) o로 · COUNT(*) → SUM(o.cnt) · SUM(o.ORD_AMT) → SUM(o.amt) · 나머지 그대로 |
| 2 | 측정 환경 | ora-hanorder · 26ai Free 23.26.2 · cpu_count 2 · PGA 512MB · 주문 1,000,000 · 고객 2,000 · 지역 8 · 통계 17화 종료 상태 (ORD_ST FREQUENCY) |
| 3 | 부하 조건 | 단일 세션 · 데우기 뒤 10회 묶음 × 3 · 스크립트 두 번. 개발 DB의 '1.2 → 0.4초'는 근거에서 뺐다(건수 미상 · 1회 · 화면 시간) |
| 4 | 현상 | rev01 HASH JOIN이 100만 행을 M_CUS 해시 테이블에 하나씩 맞춰 본다 (A-Rows 1000K) · Buffers 5,869 · 계획 2292855265 |
| 5 | 원인 | 조인을 집계보다 먼저 한다. 그래서 맞춰 보는 행 수가 곧 주문 수다. 지역명 스칼라 한 줄 때문에 옵티마이저가 Group-by Placement를 하지 않았다 (스칼라 없는 문장에서는 했다 · 16화 §13.2) · [II권 Ch4 01 p.462 · Ch2 03 (6) p.264] · [tgsql Query Transformations] |
| 6 | 개선안 | 채택: 고객별로 먼저 묶는다 (rev02). 채택하지 않은 대안: 스칼라 → 외부조인 (08화 · 지역 코드 중복을 '멈춤'에서 '조용히 두 배'로 바꾼다) · (CUS_ID, ORD_AMT) 커버링 인덱스 (T_ORD_BIG 인덱스 넷 · 14화 적재 청구서) · 결과 캐시 (15b화 · 주문이 늘 커밋된다) · SQL 프로파일 · 힌트 (계획을 묶는 것 · 일의 모양은 그대로) |
| 7 | 효과 | Buffers 5,868 → 5,868 (T0로는 0) · CPU 10회 115 ~ 121 → 32 ~ 35cs (T6 제2조) · 경과 10회 114 ~ 121 → 32 ~ 35cs · 시스템 몫: 16화 T5-001 부하 모델 Q2 = DB CPU 47.5% (운영 문장이면 더 크다) |
| 8 | 정합성 | A rev01 · B 3 = 3 · 255,398,623,700 · 0 / 0 · C 해당 (TOP N 없음) · D 두 조인 다 1쪽 1행 · E ① 이중 적재 → 둘 다 ORA-01427 ② 고아 → 같은 답 · F 해시가 달라서 더 대조 · G 변환에 기대지 않음 · H Q2 하나 → 통과 |
| 9 | 환경 의존성 | HASH GROUP BY의 work area (PGA) · 고객 수만큼의 해시 테이블 |
| 10 | 리스크 | 고객 수가 주문 수에 가까워지면 묶는 비용만 늘어난다 · 이 화면 외 영향 없음 · 부하 발생기의 Q2는 바꾸지 않는다 (16 · 17화 런 재현) |
| 11 | 롤백 | 본문을 rev01로 (객체 없음 · 즉시) |
| 12 | 재측정 | 고객 1인당 주문 10건 아래 (지금 500) · M_CUS 2만 명 초과 · PGA 변경 · 6개월 (1월 8일) · 힌트 유효기간 대장 4번 행 |
11-2. T1 — 13호 (SQL 프로파일 PROF_T18_Q2 · 기각)
| # | 칸 | 내용 |
|---|---|---|
| 1 | 대상 | Q2 rev00 (LEADING(o c) USE_NL(c)) · SQL Tuning Advisor 과제 T18_R0_Q2의 권고: SQL 프로파일 (예상 이득 99.42%) |
| 2 | 측정 환경 | STEP 2와 같다 · CONTROL_MANAGEMENT_PACK_ACCESS DIAGNOSTIC+TUNING |
| 3 | 부하 조건 | 단일 세션 |
| 4 | 현상 | 받으면 NL 1,026,849 → HASH 5,869 (2292855265 = rev01과 같은 계획) |
| 5 | 원인 | 프로파일의 정체 = IGNORE_OPTIM_EMBEDDED_HINTS · OPTIMIZER_FEATURES_ENABLE(default): 박힌 힌트를 코드 밖에서 무시한다 · [좌표 미확인] · [tgsql SQL Tuning Advisor · Managing SQL Profiles] |
| 6 | 개선안 | 기각. 대안은 코드에서 힌트를 뗀 rev01 (06화 · 이미 운영) |
| 7 | 효과 | (기각) 받았다가 지웠다: 5,869 → 1026K |
| 8 | 정합성 | 해당 없음 (SQL을 고치지 않았다 · 같은 계획) |
| 9 | 환경 의존성 | 튜닝 팩: DIAGNOSTIC · NONE에서 에러 없이 무시 (1026K) |
| 10 | 리스크 (받았다면) | 소스에 USE_NL이 남아 다음 사람을 속인다 · 정규화 서명이라 소스를 고쳐 써도 붙는다 · 계약 · 파라미터 하나로 사라진다 |
| 11 | 롤백 | DBMS_SQLTUNE.DROP_SQL_PROFILE('PROF_T18_Q2')로 지웠다 (프로파일 0) |
| 12 | 재측정 | 해당 없음. T6 제10 · 16조에 근거로 올림 |
11-3. T3 배치 SLA 리포트 — 3호 (월마감 집계 낮 재실행 · 동시 실행 영향)
| 칸 | 내용 |
|---|---|
| 1. 배치와 창 | 월마감 집계 (오과장) · 금요일 밤 강제 종료 → 월요일 낮 재실행 요청 · 마감 = 오늘 안 · 같은 시간에 온라인 화면 다섯 조각 |
| 2. 측정 환경 | ora-hanorder · Free · cpu_count 2 · NOARCHIVELOG · 동시 실행 (온라인 2세션 · 생각 0 · 배치 일꾼 2) · 5분 판 넷 |
| 3. 단계 분해 | 일꾼 = GROUP BY 월 · 고객 등급 집계 한 판 (C18_BATCH) 반복: 판당 약 12 ~ 14cs (판 B · C) |
| 7. 동시 실행 영향 ★ | 온라인 1분당 실행 41,741 (단독) → 20,497 (배치와 겹침 · −51%) · 평균 2.87 → 5.83ms · Q2 33.03 → 68.61ms / 90:10 플랜 23,754 (+16% · 첫 판 +19%) · 상한 10% 플랜 23,321 · 상한 판에서 배치 그룹 CPU 265초 vs 온라인 342초 (상한 미적용 · 배치 몫 44%) · 원인 미확인 |
| 8. 판정 | 리소스 매니저를 근거로 낮 재실행을 허락하지 않는다 (이 장비에서 잰 값이 약속과 다르다). 재실행은 19:00 이후 (건물의 화물 운행 시간표처럼 시간으로 나눈다). 운영 EE에서 같은 네 판을 재기 전에는 플랜을 근거로 쓰지 않는다 |
| 9. 재측정 | 운영 EE · 같은 네 판 · 같은 일꾼 (→ 별권) |
| 정합성 | 해당 없음 (집계 결과는 바꾸지 않았다) |
11-4. T7 워크로드 프로파일 — 3호 (WP-18R · WP-18O · WP-18N)
| # | 항목 | WP-18R (STEP 7) | WP-18O / WP-18N (STEP 8) |
|---|---|---|---|
| 1 | ID · 목적 | 낮 재실행의 영향 · 리소스 플랜의 효과 | 옛 판 셋 vs 지금 판: awrddrpt |
| 2 | 대상 · 데이터 | 17화 종료 상태 · 지문 같음 | 같다 |
| 3 | 세션 구성 | 온라인 2 · WEIGHTS=Q1=30,Q2=5,Q3=10,Q4=40,N1=14,P1=0 + 일꾼 2 (MODULE=MONTH_CLOSE) | 3세션 · 기본 가중치 (P1=1 포함) |
| 4 | 시간 구성 | 판마다 5분 · 0분째는 출발 (센 것은 1 ~ 4분) | 10분 · 스냅샷 +1 · +9분 |
| 5 | 입력값 분포 | 기본 씨앗 | 기본 씨앗 |
| 6 | 명령 | 07_격리_세_판.sh (MIN=5) | 08_옛판_지금판.sh: OLD PROFILE=SPIKE … SPIKE_AT=0 SPIKE_LEN=10 SPIKE_KIND=NL,LIT,LOOP · NOW PROFILE=STEADY |
| 7 | 성공 판정 | 에러 0 · 남은 세션 0 · 끝에 DEFAULT_PLAN | 에러 0 · 지문 앞 = 뒤 |
| 8 | 정리 | 플랜 메모리 원복 · 99가 그룹 · 플랜 · 기록 테이블 · 런 기록(C18-%)을 지운다 | 99가 런 기록을 지운다 · AWR 스냅샷은 보존 기간에 맡긴다 |
| 9 | 운영과 다른 점 | ① 일꾼은 월마감의 모양만 흉내 낸 집계 ② CPU 2 · Free의 PDB 플랜 ③ 온라인은 생각 시간 0 | ① 나쁜 판으로 바꿀 수 있는 조각은 셋(Q2 · Q4 · P1)뿐이고, Q1 · Q3 · N1은 두 구간 다 지금 판 ② 3월이 아니라 같은 날 ③ Q2는 스칼라가 없는 모델 (16화 T7 9번) |
| 10 | 리포트 | T3 3호 | T5-003 |
11-5. T5 시스템 성능 리포트 — 3호 · T5-003 (awrddrpt · 옛 판 vs 지금 판)
| # | 칸 | 내용 |
|---|---|---|
| 1 | 구간 | 같은 날 연달아: 1st 루트 스냅샷 50 – 51 (C18-OLD) · 2nd 52 – 53 (C18-NOW) · 각 8.0분 · DB time 24.0 / 24.1분 · AAS 3.0 / 3.0. 3월과 7월의 비교가 아니다 |
| 2 | 프로파일 | WP-18O · WP-18N (T7 3호) |
| 3 | 칸 | Load Profile: Logical read −94.5% · Redo −97.8% · Executes −98.5% · Parses +498% (P1 rev02의 재귀 SQL · §8.3) · CPU +8.1% / Top Events: log file switch (checkpoint incomplete) 22.9% → resmgr:cpu quantum 23.0% / Top SQL: 1st 1위 Q2 rev00 46.9% of DB time · 2nd 1위 Q2 rev01 40.0% |
| 4 | ADDM | 이 리포트에서는 돌리지 않았다 (두 구간 비교가 목적) |
| 5 | 구간 안의 분포 | 해당 없음. 두 구간 다 균일한 부하 (SPIKE를 처음부터 끝까지) |
| 6 | 전후 비교 | 같은 3세션 · 같은 10분 런에 조각 실행 6.2배 (58,640 → 363,367 · DB time은 같다) · Q2 294.56 → 40.43ms · P1 1,370 → 142ms · Q4 0.49 → 0.07ms |
| 7 | 팩 | Free · DIAGNOSTIC+TUNING (AWR 비교 리포트 = 진단 팩) |
| 8 | 결론 | ① 18주의 리비전 덕에 같은 CPU 두 개로 6배의 일을 한다. 이제 한계는 CPU 두 개다 (resmgr:cpu quantum) ② 시간 모델 1위 sequence load elapsed time 48.3%는 결론에 쓰지 않는다. ASH로 세면 시퀀스를 부르지 않는 조각의 표본에도 깃발이 켜진다 (§8.5) · 조건은 열어 둔다 |
| 9 | 정합성 | 해당 없음 (SQL을 고치지 않았다 · 부하 발생기의 판을 바꿨을 뿐) |
11-6. T2-002 갱신 — IX_ORD_BIG_DT 폐기 (04 → 14 → 16 → 18화)
선결 조건 1 AWR 계획 · 인덱스 사용 관찰 (16화 T8 ⑥) → 18화 재관찰 : DBA_INDEX_USAGE 384 회 (그 뒤 0) ·
AWR 계획 기록 스냅샷 1 ~ 53 에 0 SQL · 공유 풀 응용 SQL 0 (DS_SVC 제외)
선결 조건 2 INVISIBLE 로 한 걸음 (T2 ③) → 18화 : 6조각 + rev00 셋 + 하루 조회 — 계획 해시 변화 0 (VIS = INV)
선결 조건 3 운영 SQL 전수 조사 (소스 · 운영 AWR) → 남았다 (연습 DB 밖)
적재 비용 (14화) 5만 건마다 redo 11.70 MB · 30 %
판정 INVISIBLE 단계 승인 — 운영에서 INVISIBLE 로 내리고 한 달(월마감 · 분기 리포트 포함) 관찰 뒤 DROP.
연습 DB 에서는 VISIBLE 로 되돌렸다 (99 · 다음 사람이 같은 관찰을 다시 할 수 있게)
롤백 ALTER INDEX IX_ORD_BIG_DT VISIBLE (즉시) · DROP 뒤라면 CREATE INDEX (14화 재생성 1.5 초 남짓 몫)STEP 12 — 졸업 과제 · ora-newshop을 혼자 진단한다 (반나절) ★★★
혼자 한다. 이 절에는 질문만 있다. 답은 맨 아래 봉인 상자와 🔒
E90·E91에 있다. 자기 원장과 리포트를 다 쓴 뒤에만 연다. 설명서 두 개(이커머스_ERD_조감도.md,이커머스_데이터셋_구축가이드.md)를 먼저 읽는다. 그리고 믿지 않는다. 두 문서 머리의 🔒 정정 상자는 과제를 끝낸 뒤에 연다.
12-0. 준비
bash
docker start ora-newshop
MSYS_NO_PATHCONV=1 docker exec -i ora-newshop sqlplus -s / as sysdba < 실습/졸업과제/E00_측정권한_SYSDBA.sql
docker exec -i ora-newshop sqlplus -s ecommerce/ecommerce@localhost/FREEPDB1 < 실습/졸업과제/E01_현업_원본_재기.sqlE01은 현업이 넘긴 넷(E1_rev00 ~ E4_rev00)을 고치지 않고 T0로 잰다. 답은 말하지 않고 숫자만 낸다.
| 현업 SQL | 넘긴 사람의 말 |
|---|---|
E1_rev00.sql 고객센터 「주문 내역」 | "하루 종일 누르는 화면인데 한참 걸린다. 가끔 고객이 '내 주문이 없다'고 하는데, 고객이 착각하는 것 같다." |
E2_rev00.sql 마케팅 「월간 카테고리 매출 TOP 5」 | "느린 건 괜찮다. 재무팀이 우리 매출이 월마감과 안 맞는다고 하는데, 재무팀이 잘못 보는 것 같다." |
E3_rev00.sql CRM 「고객 등급 재산정」 야간 배치 | "고객이 늘면서 길어진다. 서버가 작아서 그런 것 같으니 증설을 검토해 달라." |
E4_rev00.sql 상품 상세 「최근 리뷰 10건」 | "인기 상품일수록 리뷰 칸이 늦게 뜬다." |
12-1. 해야 할 것 (산출물)
| # | 산출물 | 양식 | 통과 조건 |
|---|---|---|---|
| 1 | 설명서 대조표: 문서에 적힌 것 · 데이터 사전에 나온 것 · 왜 달랐나 | 자유 (세 열) | 다른 줄이 최소 넷 · 각 줄에 그 사실을 보인 쿼리 |
| 2 | E 원장: E1 ~ E4의 rev00 (W01의 모양 · 조건 · 규모) | W01 | 측정 조건 칸 · 데이터 규모 칸이 차 있다 |
| 3 | T1 리포트 넷 (E1 ~ E4 · rev00 → rev01) | T1 12칸 | 8번 정합성 체크리스트 A ~ H, 특히 D (1쪽) · E (경계) |
| 4 | T3 리포트 (E3) | T3 | '증설' 제안에 대한 답 (T6 제9조) |
| 5 | T2 승인서 (만든 인덱스 전부) | T2 | INVISIBLE · 크기 · CF · 적재 비용 (redo) · 롤백 |
| 6 | 설명서 정정안: 지우지 않고 옆에 | 문서 운용 (T6 §5) | 정정 표시 · 근거 쿼리 |
| 7 | 현업 회신 넷 (각 400자 안) | — | 현업의 가설("고객이 착각" · "재무팀이 잘못" · "서버가 작다")에 숫자로 답한다 |
| 8 | T6 개정 제안 (선택) | — | 자기 리포트가 대기 줄 후보의 두 번째 근거가 되는가. 되면 v1.1 안 |
규칙. 원본 30개 테이블에는 한 줄도 쓰지 않는다(결과 테이블 · 복사본 · 인덱스는 C18 이름표를 붙이고, E99가 지운다). 인덱스는 INVISIBLE로 만들고 세션에서만 켜서 잰다(OPTIMIZER_USE_INVISIBLE_INDEXES). 꺼진 제약은 켜지 않는다(고객사가 정할 일이다).
12-2. 채점표 (100점 · 스스로 채점한 뒤 봉인을 연다)
| 영역 | 배점 | 만점의 조건 |
|---|---|---|
| 설명서를 믿지 않았다 | 15 | 제약을 상태(STATUS · VALIDATED)까지 셌다 · 행수를 다시 셌다 · 히스토그램을 확인했다 · 차이를 정정안으로 |
| 고아 · 1쪽 | 15 | 꺼진 제약 아래에서 실제로 일치하지 않는 키를 찾았다 · E1의 '고객 착각'을 숫자로 반박했다 |
| 정합성 회귀 | 20 | E1 · E2 · E3를 원장과 대조 (직전 판과만이 아니라) · 건수 · 합계 · MINUS 양방향 · 경계 |
| 성능 | 15 | rev00 → rev01을 T0로 · E3를 횟수(재귀 호출 · 실행 수)로 |
| 비용과 위험 | 10 | 인덱스의 DML 비용(redo)을 쟀다 · INVISIBLE · 롤백 |
| 전제 · 규모 (마지막 조항) | 15 | 모든 숫자 옆에 조건과 규모 · 재측정 조건 |
| 현업 회신 | 10 | 가설에 숫자로 · 모르는 것은 모른다고 |
| 감점 | — | 원본에 씀 −30 · 제약을 켬 −20 · 인덱스를 VISIBLE로 둠 −10 · E99를 안 돌림 −10 |
🔒 모범 답안: 자기 원장 · 리포트 · 채점을 다 끝낸 뒤에만 펼친다
숫자는
ora-newshop(L4 카드의고정_실행.py로 만든 결정적 데이터 · 26ai Free 23.26.2)에서 잰 값이다. 같은 방법으로 만들었다면 같은 숫자가 나온다. 확인 스크립트는E90_모범답안_조사.sql과E91_모범답안_개선.sql이다.
① 설명서 대조 (E90)
| 설명서 | 데이터 사전 · 데이터 | 왜 |
|---|---|---|
| FK 제약 0 · 논리적 관계 | R 39개 · 전부 DISABLED NOT VALIDATED | install.sql은 FK를 만든다. SQL*Loader direct=true가 적재 전에 끄고 다시 켜지 않는다 (적재 로그 "Constraint … was disabled and novalidated before the load" · "No CHECK, REFERENTIAL constraints were re-enabled after the load") |
| CHECK 8종 | CHECK 8개 DISABLED NOT VALIDATED (NOT NULL 94개는 켜짐 · C 합 102) · 지금 데이터의 위반 0 | 같은 이유 |
| PK · UQ 인덱스만 · 보조 0 · 뷰 · 트리거 · 파티션 0 | 맞다: 유니크 인덱스 34 + LOB 4 = 38 · P 30 · U 4 · 객체 101 | — |
| 합계 ≈ 484,798 · 행수 표 | 445,422 · 7개 테이블의 행수가 표와 다르다 (721 · 704 · 7,253 · 73,498 · 25,024 · 455 · 571) | 행수 표는 생성기의 옛 출력이다 |
end_date 기본 9999-12-31 (SCD2) | DDL 기본값은 맞다 · 그 값인 행 0 · 마지막 end_date 2024-12-23 | CSV가 값을 채웠다. '지금 등급'을 이 표에서 찾으면 0명 |
| 편향 컬럼(grade · status)에 히스토그램 | orders.status · customers.grade · payments.payment_method · point_transactions.tx_type NONE · product_id 류 HYBRID · parent_id · category_id 등 FREQUENCY | SIZE AUTO는 조건절에 쓰인 적 있는 컬럼에만 만든다 |
| (FK가 없으니) 조인 키는 일치 | 고아: shipping.address_id 2,719행 (고객 2,103명)이 customer_addresses에 없다 (주소 번호 1 ~ 7,845 · 주소 표 1 ~ 7,253) · FK를 켜 보면 ORA-02298 · 남의 주소로 간 배송 32,178행 | 꺼진 FK는 검사하지 않는다 |
| (말 없음) | orders.total_amount ≠ 그 주문의 SUM(quantity × unit_price): 34,908건 전부 · 주문의 시분초 있음 34,907 / 34,908 · 주문이 하나도 없는 고객 4명 · 리뷰는 상품마다 34 ~ 76건 (평균 53.6) · 결제 · 배송은 주문과 1 : 1 (34,908) | 주문 금액 칸은 상세의 합이 아니다. 재무팀과 마케팅이 서로 다른 칸을 본다 |
② E1: 주문 내역 (고객 390)
| rev00 | rev01 | |
|---|---|---|
| 모양 | 4개 테이블 내부 조인 | 결제 · 배송 · 주소를 외부 조인 + IX_C18_ORD_CUST_DT (customer_id, order_date) · IX_C18_PAY_ORD · IX_C18_SHP_ORD (INVISIBLE · 세션에서만) |
| Buffers · 계획 | 574 · 3165474862 | 116 · 3495127350 |
| 행 | 12행(그 고객의 주문은 16건) | 16행 |
| 전체 고객 | 32,189행 | 원장(주문) 34,908 = 34,908 |
고객이 착각한 것이 아니다. 배송 주소가 고아인 주문은 내부 조인에서 사라진다(34,908 − 32,189 = 2,719 = 고아 배송 행). 회신은 이렇게 쓴다. "고객님의 주문 4건이 화면에서 빠졌던 것은 배송 주소 기록이 끊어진 주문이라서입니다. 전체로는 2,719건입니다."
③ E2: 월간 카테고리 매출 (2024-06)
| rev00 | rev01 | |
|---|---|---|
| 모양 | TO_CHAR(order_date,'YYYYMM') = :ym · 리뷰를 상품에 바로 조인 (M : 1 : M) | 기간은 >= 월초 AND < 다음 달 월초 · 리뷰 평점은 상품별로 먼저 묶어 붙인다 |
| Buffers · 계획 | 1,230 · 2962231673 | 537 · 656350341 |
| 매출 | TOP1 "Decision Along" 470,514,433 · 전 카테고리 합 9,644,681,986 = 원장의 53.55배 | 원장 6월 180,105,479 (상세 1,166 · 주문 563) · TOP1 "Around Prevent" 8,992,834.43: 1 · 2위가 바뀐다 (배수가 카테고리마다 50.7 ~ 54.6이라서) |
재무팀이 잘못 본 것이 아니다. ① 리뷰 수만큼 매출이 불어난다(08화의 Q3와 같은 구조이고, 이번에는 배수가 고르지 않아 순위가 뒤집힌다). ② 재무가 보는 orders.total_amount(6월 138,384,434)는 상세의 합과 다른 칸이다. 어느 쪽이 '매출'인지는 현업 둘이 정할 일이다(13화 · 재협상). ③ 시분초가 있어서 BETWEEN … AND 월말일로 쓰면 주문이 563건이 아니라 550건이 된다(02화의 함정 · 코딩 가이드 대기 조문 7의 두 번째 근거).
④ E3: 등급 재산정 배치
| rev00 (커서 루프) | rev01 (한 문장) | |
|---|---|---|
| 경과 · CPU | 589 ~ 606cs | 2 ~ 3cs |
| 논리 읽기 | 2,039,674 ~ 2,040,085 | 1,084 ~ 1,085 (첫 판 1,150 ~ 1,380) |
| 재귀 호출 · 실행 수 | 약 10,600 ~ 10,900 · 약 10,500 ~ 10,600 | 1문장 |
| 정합성 | 5,230 = 5,230행 · 2,247,555,570 = 2,247,555,570 · MINUS 0 / 0 · 중복 0 | |
| 경계 | 기간 안 주문이 없는 고객 1,390명 · 주문이 아예 없는 고객 4명: 내부 조인으로 한 문장을 만들면 3,840행 (규칙 위반) → 외부 조인 | |
| 결과 | BRONZE 2,634 · SILVER 1,592 · GOLD 830 · VIP 174 · 등급이 바뀌는 고객 3,136 |
서버가 작은 것이 아니다. 고객 수만큼 SQL이 건너간다(T6 제6조 · 15화). 증설로 줄일 수 있는 것은 CPU 대기뿐인데, 이 배치의 문제는 대기가 아니라 일의 양이다(T6 제9조).
⑤ E4: 최근 리뷰 10건 (상품 100)
rev00 757 · 515998181 → IX_C18_REV_PROD_DT (product_id, created_at, rating) (INVISIBLE) → rev01 59 · 1529075933 · 같은 날 리뷰 동점 0 (정렬 키 유일성 확인, 09화).
⑥ T2: 인덱스 넷의 값 (E91)
| 인덱스 | leaf · CF · 크기 | 만드는 시간 |
|---|---|---|
IX_C18_ORD_CUST_DT | 112 · 34,730 · 1,024KB | 0.04 ~ 0.15초 |
IX_C18_PAY_ORD | 77 · 180 · 704KB | 〃 |
IX_C18_SHP_ORD | 77 · 171 · 704KB | 〃 |
IX_C18_REV_PROD_DT | 54 · 14,981 · 512KB | 〃 |
| 적재 비용 (주문 복사본 34,908행 INSERT) | 인덱스 없이 redo 1.56MB · 블록 변경 1,816 → 인덱스와 함께 12.02 ~ 12.24MB · 72,281 ~ 72,947 |
⑦ T6 개정 제안: E1(고아 키 · 1쪽에 없다)과 E2(리뷰 M : 1 : M)는 코딩 가이드 대기 조문 9의 두 번째 근거이고, E2의 BETWEEN은 대기 조문 7의 두 번째 근거다. 자기 T1 리포트 번호를 각주에 달아 v1.1로 올리는 개정안을 쓴다. 승격 규칙을 표준 자신에게도 적용하는 것이다.
12-3. 끝나면 — 봉인 상태로
bash
MSYS_NO_PATHCONV=1 docker exec -i ora-newshop sqlplus -s / as sysdba < 실습/졸업과제/E99_정리_SYSDBA.sql기대: C18 객체 0 · E18 커서 0 (다시 돌려도 DISPLAY_CURSOR가 계획을 찾게) · 테이블 30 · 행 445,422 · 인덱스 38 · 객체 101 · FK 39 DISABLED · CHECK 8 DISABLED · ecommerce 권한 6 · 롤 0. 처음 받은 그대로다. (ORA_SQL_TEST는 건드리지 않는다.)
STEP 13 — 막힘 로그 결산 · 파인만 자가설명
결산 (01화부터의 약속). _막힘로그.md를 연다.
- 같은 항목이 3회 이상 나온 줄을 재학습 큐로 옮긴다.
- 큐의 항목마다 그때 다녀온 좌표로 돌아가 백지에 다시 설명한다(4단). 막히면 좌표가 틀렸거나 모자란 것이니, 원천지도에 한 줄을 더한다.
- 로그가 비어 있다면 18주 동안 막힘을 옮겨 적지 않았다는 뜻이다. 그럴 때는 오늘 만든 표준 16조를 하나씩 백지에 설명해 본다. 설명이 끊기는 조문이 곧 재학습 큐다. 그 조문의 근거 리포트를 쓴 화의
2_파고들기로 돌아간다.
| 태그 | 전형적인 문장 (오늘 버전) |
|---|---|
#인과 | "베이스라인이 있는데 왜 ENABLED를 내리자 NL이 돌아왔는지 말로 못 하겠다" → §2.4 |
#용어 | "프로파일 · 베이스라인 · 힌트가 계획을 '지키는' 방식의 차이" → §2.1 · §4.3 |
#수치 | "Buffers가 같은데 CPU가 3분의 1인 이유를 숫자로 못 보이겠다" → §6 |
#절차 | "팩이 없는 서버에서 프로파일이 사라진 것을 어떻게 알아채나" → §5 |
4단 되묻기 3문 (오늘 버전)
- 조건 반전: "
M_CUS가 200만 명이 되면 Q2 rev02는 rev01보다 여전히 나은가? 무엇을 재면 아는가?" - 반례 요구: "SQL 프로파일이 옳은 선택인 현장은? 그때도 T6의 어느 조문이 무엇을 적으라고 하나?"
- 측정 지목: "리소스 플랜이 '지켜졌다'고 말하려면 무엇을 재야 하나? 이 화에서 잰 셋을 대라."
정리 — 17화 종료 상태로
bash
MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < 실습/99_정리_SYSDBA.sql기대 (마지막 표): 베이스라인 0 · 프로파일 0 · 진화 · 튜닝 과제 0 · C18 · W18 객체 0 · C18 플랜 · 그룹 0 · resource_manager_plan DEFAULT_PLAN (spfile 지정 없음) · IX_ORD_BIG_DT VISIBLE · 팩 DIAGNOSTIC+TUNING · sqlp 권한 ALTER SESSION · SELECT ANY DICTIONARY 둘 · 런 기록 C18-% 0 (17화까지의 20개는 그대로) · 원본 지문이 STEP 0과 같다.
남기는 것: AWR 스냅샷(보존 기간 8일이 지나면 지워지게 둔다) · 17화의 ADDM 과제 · 17화까지의 런 기록. 지우는 것: 이 화가 만든 것 전부. 지울 수 없는 흔적(설정의 수정 시각 등)은
2_파고들기.md §12에 적었다.
완료 체크리스트
- [ ] STEP 0: 전제 · 원본 지문 · 오늘 쓸 것 0 · 원장의 전제 줄에 동그라미
- [ ] STEP 1: 보기 전에 예측 6개를 적었다
- [ ] STEP 2: W01 원장 2열 · 조각마다 '그때는 왜 옳았고 지금은 왜 다른가' · 01화 표는 그대로
- [ ] STEP 3: 베이스라인이 좋은 계획도 나쁜 계획도 막는 두 장면 · 진화 둘 ·
ORA-38153 - [ ] STEP 4: STA 여섯 과제의 권고 · 프로파일의 두 줄 · 서명 · 지운 뒤 1026K
- [ ] STEP 5: 팩 네 판 · 프로파일이 에러 없이 사라짐 · 되돌렸다
- [ ] STEP 6: 초안에 빠진 것 다섯 줄 · Q2 rev02의 CPU 6묶음 · 정합성 · 경계 둘
- [ ] STEP 7: 네 판 · 「동시 실행 영향」 칸 · 끝에
DEFAULT_PLAN - [ ] STEP 8: awrddrpt · DB time이 같은 이유 · 6배 · 결론에 쓰지 않은 줄
- [ ] STEP 9: T2-002의 세 증거 · 결과 캐시 셋 · 체크리스트 '시각 · 세션' 줄
- [ ] STEP 10: T6 v1.0 · 코딩 가이드 v1.0, 직접 센 표와 대조
- [ ] STEP 11: T1 12 · 13호 · T3 3호 · T7 3호 · T5-003 · T2-002 갱신
- [ ] STEP 12: 졸업 과제, 산출물 여덟 · 채점 · 봉인은 그 뒤에 열었다 ·
E99 - [ ] STEP 13: 막힘 로그 결산 (또는 표준 16조 백지 설명)
- [ ] 정리:
99· 팩 · 플랜 · 인덱스 · 권한 · 지문
완결성 게이트: STEP 2의 원장 2열 · STEP 10의 표준 · STEP 12의 졸업 과제와 STEP 5 · 7 · 정리의 원복 확인이 비면 이 화는 미완성이다.
이 화가 남기는 상태
| 무엇 | 값 |
|---|---|
ora-hanorder 원본 | 변화 없음: 17화 종료 상태 (99 뒤) |
ora-newshop | 봉인 상태: 테이블 30 · 445,422행 · 꺼진 FK 39 · CHECK 8 · ORA_SQL_TEST 그대로 (E99 뒤) |
| 남긴 것 | AWR 스냅샷 · 17화까지의 런 기록 · 지울 수 없는 흔적 (2_파고들기 §12) |
| 인스턴스 설정 | 팩 DIAGNOSTIC+TUNING · 리소스 플랜 DEFAULT_PLAN · AUTO_SPM_EVOLVE_TASK OFF |
| 리비전 | Q2 rev02 (18화의 유일한 리비전) |
| 새 문서 | T6 사내 튜닝 표준 v1.0 · SQL 코딩 가이드 v1.0 · W01 원장 2열 · 리비전 대장 · 힌트 유효기간 대장 졸업 재측정 · 이커머스 설명서 정정 둘 · 정합성 체크리스트 '시각 · 세션' 줄 |
| 열어 둔 것 | IN_SEQUENCE_LOAD 깃발의 조건 · 리소스 플랜이 비율을 지키지 않은 이유 · 운영의 결과 캐시 크기 · IX_ORD_BIG_DT의 운영 소스 전수 조사 |
이 교재는 여기서 끝난다. Free에서 잴 수 없었던 것(병렬 · RAC · 운영 EE의 리소스 매니저 · 자동 SPM)은 별권의 몫이다.