테마
17화 · 평균은 거짓말을 한다
🗓️ 7월 1일 (월) · 16화에서 일주일 뒤 · 무대
ora-hanorder(한빛유통) · 4막의 둘째 화오늘 배울 것: 평균은 '얼마나'를 말하고 '언제'를 버린다. 지난주 AWR 한 시간 리포트로 낸 결론("문제는 CPU를 먹는 일의 양, 락은 부차적")을 ASH 표본으로 시간 축에 펼쳐 놓고 뒤집는다. 이어서 ADDM의 권고를 표본으로 검증하고, 팩이 없는 서버에서는 Statspack으로 같은 일을 한다. 이번 화에서는 조각을 고치지 않는다(리비전 없음). 산출물은 T5 2호(교차검증판)와 진단 도구 4열 대응표다.
파일 세 개
| 파일 | 무엇을 하나 | 시간 | |
|---|---|---|---|
| 읽는다 | 1_사건.md | 조민우 씨가 "그게 부차적인 건가요"라고 묻는다. 방재실 녹화기를 보고, 지난주 한 시간을 10초 표본으로 펼쳐 5분짜리 폭발을 찾고, 문을 잡은 세션이 녹화에 없다는 것을 확인한다. 같은 한 시간을 다시 만들어 1초 단위로 보고, ADDM 1위 권고를 그대로 따라 해 본 뒤, 팩이 없는 서버 앞에서 손을 멈추는 하루 | 40분 |
| 한다 | 3_실습과_기록.md | 10초 표본 · 62분 런 · 1초 표본 · 블로킹 체인 · ASH 리포트 · ADDM 검증 · SQL Monitor · NONE · Statspack · T5-002 · T7-002 · 4열 대응표 | 2~2.5시간(62분 런 포함) |
| 찾는다 | 2_파고들기.md | ASH의 칸 · 표본 수학 · 메모리와 디스크 · 시간 축으로 자르기 · 16화 결론이 뒤집힌 넷 · 블로킹 체인의 시계열 · ASH 리포트의 함정 · ADDM 검증법 · Sequence Usage의 정체 · SQL Monitor · NONE 실측 · Statspack | 필요할 때 |
1을 읽고 3을 따라 한다. 2는 막힐 때 펼친다. 시간이 없는 주에는 1_사건을 중단 지점 1까지 읽고 3_실습은 STEP 0, 1, 3, 11만 한다(약 40분). 런을 다시 돌리지 않는 길이라서, 16화 STEP 9의 한 시간이 아직 AWR 보존 기간 8일 안에 있을 때만 쓸 수 있다.
지난 화에서 들고 오는 것
| 16화 | T5-001: 한 시간(WP-16H) 리포트 한 장으로 낸 결론. DB Time 181.09분 · 평균 활성 세션 3.02 · DB CPU 1.84 · CPU 대기 23.8% · enq: TX 5.5%는 부차적(599초 · 853번 · 평균 702ms) · Row Lock Waits의 88%는 SYS.OPTSTAT_HIST_CONTROL$ · FOR UPDATE SQL은 목록에 없다 · 조민우 씨의 "10시 반"은 확인할 수 없다 |
| 16화 | 읽지 않은 셋: WP-16H의 명령(선배가 정했다 · 무엇이 들었는지는 열지 않았다) · 리포트 뒤쪽의 ASH · ADDM 절 · 발생기의 분별 기록 |
| 16화 | ADDM 요약 1위 Sequence Usage · SQL 경과 시간과 230배 어긋난 Time Model sequence load 칸 · "맞지 않는 칸으로는 결론을 내지 않는다" |
| 15화 | 부하 발생기 PKG_LOAD_DRIVER는 결정적이다(같은 씨앗이면 같은 분에 같은 모양) · SPIKE_KIND=LOCK은 N1 핫픽스(FOR UPDATE)에 더해 세션 1이 번호 행을 HOLD_SEC초씩 쥐는 모양 |
| 12화 | 블로킹 트리(V$SESSION · 지금 이 순간만 보인다) · V$LOCK의 TX 이름표 · T4 1호 재발방지 3번 블로킹 트리 1분 감시 |
그리고 월요일 아침에 메시지가 두 통 와 있다.
"T5-001 받았어요. 6/17 10시 반에는 정말로 화면이 다 멈췄어요. 넷이 동시에 등록 버튼 누르고 기다렸고, 5분쯤 지나니까 한꺼번에 넘어갔어요. 그게 부차적인 건가요?" (조민우, 08:47) "증설은 보류. 그리고 인수한 버거 프랜차이즈 POS DB도 AWR 한 장 뽑아 봐 주세요." (박팀장, 09:02)
오늘의 전개 — 지난주의 내가 틀린 자리를 잰다
| 내가 믿은 것 | 측정(연습 DB · 같은 한 시간 · 3세션 · 생각 시간 0) | |
|---|---|---|
| 09:20 | "평균 702ms, 1초도 안 되는 기다림으로 5분이나 멈추진 않는다" | 853번 = 8~16초 60번 + 4ms 미만 793번. 702ms짜리 대기는 한 번도 없었다 |
| 10:10 | (10초 표본) | 분마다 표본 18 · 10:30~10:34 다섯 칸만 12(전부 enq: TX, CPU 0) · 5분 창 100% vs 한 칸 5.7% · 표본 60 × 10 = 600 ≈ AWR 599.0초 |
| 10:40 | "막은 세션을 보면 뭘 했는지 알겠지" | 막은 세션(지난주 런의 세션 1)의 표본: 폭발 전 186 · 중 0 · 뒤 156 · ASH 리포트 BLOCKING SESSION NOT FOUND 0/30. 쉬면서 행을 쥐고 있었다 |
| 11:15 | (지난주 자동 ADDM) | Row Lock Waits의 Rationale은 session 175 · 62822 = 100%(ASH와 같다) · 그러나 Impact .19로 4위(평균) · '88%'는 횟수 |
| 12:10 | (같은 한 시간을 다시 · 1초 표본) | 두 런이 같은 모양 · 1분에 180 → 120 · 10초마다 20(S2와 S3가 S1에게 막힘) · 대기 30 × 10초씩 · TX 30개 · 과거의 트리는 뿌리만 표본이 없다 |
| 13:30 | (왜 뒤집혔나) | 평균 · 칸 · 단위 · 횟수. 그리고 그 5분은 16화 AWR 리포트 뒤쪽 ASH 절의 Activity Over Time에 처음부터 있었다 |
| 14:10 | "ADDM 1위 권고(시퀀스 캐시 키우기)대로 하면 줄겠지" | CACHE 20 → 1000: DB time 900.7 → 902.2초 · ADDM이 같은 권고를 다시 낸다 · 시퀀스를 안 불러도 깃발이 켜진다 · 기각 |
| 15:00 | "10초 넘게 기다린 FOR UPDATE는 SQL Monitor에 있겠지" | 없다. 5초는 CPU + I/O만 센다 · 8초짜리 CPU 문장은 감시된다 · MONITOR 힌트 ○ · Free에서 병렬은 직렬로 돈다 |
| 15:40 | (팩 없는 서버에서 습관처럼 awrrpt) | SE2라 진단 팩을 살 수 없다 · NONE 실측: ADDM ORA-13716 · ASH 표본 0 · SQL Monitor 감시 0 · 그러나 AWR · ASH 리포트와 DBA_HIST 조회는 된다(그래도 위반) · Statspack 한 시간 5.7% · 5분 스냅샷 99.5% |
왜 이렇게 짰을까? 16화에서는 "칸 하나는 결론이 아니다"를 배웠다. 그런데 그 화의 마지막 결론은 칸마다 근거를 붙였는데도 틀렸다. 근거가 전부 평균이었기 때문이다. 이번 화에서는 같은 한 시간을 결정적인 부하로 다시 만들어, 지난주 결론을 지우지 않고 그 옆에 정정을 단다. 리포트를 고치는 법이 아니라 리포트를 의심하는 순서를 배우는 화다.
오늘 배우는 한 문장
평균은 '얼마나'를 말하고 '언제'를 버린다. 한 시간을 한 칸으로 셀 때 5%인 것이 5분 칸에서는 100%일 수 있다. 칸을 다시 자를 수 있는 것은 순간을 남긴 기록(ASH)뿐이고, 그런 기록이 없는 곳에서는 칸을 미리 좁혀 두어야 한다.
| 지난주 리포트의 숫자 | 실제로는 무엇이었나 | 오늘 다시 센 것 |
|---|---|---|
enq: TX 5.5% | 한 시간 평균 | 10:30~10:35 100% · 나머지 55분 0% |
| 평균 대기 702ms | 두 무리의 평균 | 10초 × 60번 + 4ms 미만 × 793번 |
| 행 락의 88% = SYS 통계 테이블 | 횟수 | 시간은 0초 · 시간의 100%는 채번 작업 테이블 |
| DB CPU 1.84 | 한 시간 평균 | 55분 2.01 + 5분 0 |
| ADDM 1위 Sequence Usage 88% | 꺼지지 않는 깃발 | 그 시퀀스를 쓰는 INSERT는 수십 초 · 권고대로 해도 그대로 |
🔑 오늘의 비유 — 지하 2층 방재실의 녹화기
| 방재실 | 오라클 |
|---|---|
| 검침 대장: 매시 정각에 한 줄(16화) | AWR: 두 스냅샷의 뺄셈 · 구간 평균 |
| 녹화기: 1초에 한 장 · 움직이는 것만 | ASH: 1초에 한 번 · 활성 세션만 |
| 장마다 줄 선 사람 수를 더하면 사람 × 초 | 표본 수 = DB Time 초(표본 수학) |
| 원본 하드는 하루쯤 지나면 덮어쓴다 | V$ACTIVE_SESSION_HISTORY: 고리 버퍼(이 DB는 4MB · 3세션이면 1시간도 못 담는다) |
| 열 장에 한 장씩 떼어 일주일 넘게 보관 | DBA_HIST_ACTIVE_SESS_HISTORY: 10개에 1개 · AWR 보존 8일 |
| 엘리베이터가 5분 서 있던 시간: 대장에는 '전기를 조금 덜 쓴 조용한 한 시간' | 폭발 5분: AWR에는 DB CPU 1.84 · 락 5.5% |
| 엘리베이터 안에는 카메라가 없다. 문을 잡은 사람은 녹화에 없다 | 쉬면서 행을 쥔 세션은 표본에 없다. 막힌 쪽의 BLOCKING_SESSION에 이름만 남는다 |
| 줄 선 사람들이 쳐다보는 8층 표시등 | BLOCKING_SESSION · BLOCKING_SESSION_SERIAL# · P1/P2/P3(TX 이름표) |
앞에서 나온 비유도 다시 쓴다. 16화 검침 대장(오늘은 그 대장의 한 줄이 무엇을 묻어 버렸는지 본다)과 12화 총무팀 문서 발번 대장(번호 행 하나에 줄을 서는 모양 그대로)이다.
☠️ 오늘의 함정 — 근거가 있는 틀린 결론
'락은 한 시간의 5.5 %' → 사실. 그 599초는 300초 안에 있었다 (5분 창 100 %)
'평균 702 ms — 1초도 안 된다' → 사실인 평균. 702 ms 짜리 대기는 한 번도 없었다
'행 락의 88 % 는 SYS 통계 테이블' → 사실인 횟수. 시간으로는 0 초
'ADDM 1위 — 시퀀스 캐시를 키워라' → 권고대로 해도 그대로. 시퀀스를 안 불러도 깃발이 켜진다
'NONE 이면 막아 주겠지' → AWR · ASH 리포트 · DBA_HIST 는 그대로 열린다. 라이선스는 계약이 지킨다
'ASH 리포트 한 시간' → l_data_src 를 빼면 표본 313 · AAS 0.87 · enq: TX 없음 (에러 없이)
'막은 세션은 ASH 에 있겠지' → 쉬면서 쥐면 0 장. 이름만 남는다실습 준비
bash
docker start ora-hanorder16화를 마친 상태에서 시작한다. 실습/00_준비와_점검.sql이 이 상태를 확인한다(15화 종료 상태 그대로 · PKG_LOAD_DRIVER VALID · 도는 드라이버 세션 0 · C16 복사본 0 · 드라이버 시퀀스 SQ_STLM_LD CACHE 20 · 16화 런 기록 C16-H와 그 AWR). 부하 발생기는 다시 설치하지 않는다.
PowerShell에서는 먼저
$OutputEncoding = New-Object System.Text.UTF8Encoding $false를 한 줄 실행한다. SYS 스크립트(_SYSDBA)는 Git Bash에서MSYS_NO_PATHCONV=1 docker exec -i ora-hanorder sqlplus -s / as sysdba < …로 돌린다.
| 스크립트 | 무엇 | 계정 | 소요 |
|---|---|---|---|
실습/00_준비와_점검.sql | 전제 · 원본 지문 · 지난주 한 시간이 디스크에 남았나 · 메모리 ASH의 범위 | sqlp | 수 초 |
실습/01_Statspack_설치_SYSDBA.sql | Statspack을 FREEPDB1에 설치(있으면 지우고 다시) | SYS | 수 초 |
★★ 실습/02_구간_스냅샷_SYSDBA.sql + 발사기 | 같은 한 시간(C17-H): AWR +1 · +61분 · Statspack 5분마다 | SYS + sqlp | 62분 |
★ 실습/03_지난주_한시간_10초표본.sql | 지난주 한 시간: 분 · 5분 · 한 칸 · 표본 수학 · 막은 쪽 | sqlp | 수 초(런 동안) |
실습/03b_지난주_ADDM_SYSDBA.sql | 지난주 한 시간의 자동 ADDM | SYS | 수 초(런 동안) |
★ 실습/04_이번주_한시간_1초표본.sql | 이번 주: 1초 표본 · 세 줄 나란히 · 표본 수학 | sqlp | 런 끝나자마자 |
★ 실습/05_블로킹_체인_시계열.sql | 10초마다 본 체인 · 과거의 트리 · TX 이름표 | sqlp | 수 초 |
실습/06_ASH_리포트_SYSDBA.sql | ashrpt와 같은 리포트: 한 시간(디스크) · 1분 칸 · 폭발 5분 | SYS | 수 초 |
★ 실습/07_ADDM_검증_SYSDBA.sql | addmrpt와 같은 리포트 + Rationale을 ASH로 검증 | SYS | 10초 |
실습/08_시퀀스와_행락_재기.sql | ADDM 1위 권고와 AWR '행 락 88%'를 혼자 다시 재기 | sqlp | 20초 |
실습/08b_권고_적용_런_SYSDBA.sql | (선택) 권고를 부하 위에서 시험: CACHE 20 vs 1000 | SYS + sqlp | 7분 × 2 |
실습/09_SQL_Monitor.sql | SQL Monitor 시작 조건: 기다림 · 5초 · 힌트 · 병렬 | sqlp | 20초 |
★ 실습/10_팩_없는_현장_SYSDBA.sql | NONE(메모리만) → 무엇이 막히나 → 되돌린다 | SYS | 20초 |
★ 실습/11_Statspack_리포트_SYSDBA.sql | spreport: 한 시간 · 10:30~10:35 | SYS | 수 초 |
실습/99_정리_SYSDBA.sql | 팩 설정 확인 · PERFSTAT 지우기 · 16화 종료 상태 확인 | SYS | 수 초 |
두 번 돌려도 판정은 같다. 같은 한 시간을 16화와 17화에 걸쳐 세 번 돌렸다(16화 1번, 17화 2번). 분 칸과 5분 창의 모양, 막은 세션, 대기 길이, ADDM의 판정은 세 번 모두 같았고, 실행 횟수와 초, 퍼센트만 조금씩 흔들렸다(범위는
2_파고들기 §14).00,01,03,07,08,09,10,11은 두 번씩 돌려 같은 결과가 나오는 것을 확인했다. 원본 테이블에는 한 줄도 쓰지 않는다. 인스턴스 설정은 하나만 바꿨다가 되돌린다.10에서CONTROL_MANAGEMENT_PACK_ACCESS를 메모리에서만 바꾼다. ℹ️ Docker로 띄운 Oracle Database Free 한 대로 모든 실습을 마친다. Free에는 진단 팩과 튜닝 팩이 들어 있어서, '팩 없는 서버'는 설정값을 메모리에서만NONE으로 내려 흉내 냈다. Free에는 병렬 실행이 없어 SQL Monitor의 '병렬이면 저절로 감시' 조건은 잴 수 없다. 대규모 EE 환경은 별권에서 다룬다.
이 화의 산출물
| 무엇 | 어디 |
|---|---|
| T5 시스템 성능 리포트 2호(T5-002 · 교차검증판): 1호를 고치지 않고 옆에서 정정 | 3_실습 STEP 11 · T5 양식 (17화 개정: 4 · 5 · 7번) |
| 진단 도구 4열 대응표: AWR ↔ ASH ↔ ADDM ↔ Statspack(제정 · 1판) | 40_보고서와_표준/템플릿/진단도구_4열_대응표.md |
| T7 워크로드 프로파일 2호(WP-17H · WP-17S) | STEP 11-1 |
리비전/ | 없다. 이 화는 SQL을 고치지 않는다(진단만 한다) |
이 화가 다음으로 넘기는 것
| 무엇 | 어디로 |
|---|---|
| 진단 도구 4열 대응표와 질문 셋(평균인가 · 횟수인가 · 언제인가)을 사내 튜닝 표준(T6) 조문 후보로 | 18화 |
| ADDM Top SQL Statements의 권고: SQL Tuning Advisor · Q2 보류안(08 · 16화) · SPM | 18화 |
| T5-002의 규칙: ADDM 권고는 Rationale을 표본으로 다시 센 뒤에 채택한다 · 한 시간 리포트로 현업의 '몇 분'을 기각하지 않는다 | 18화 |
| 그동안 쓴 리포트 전부: 지난 결론을 지우지 않고 옆에 정정을 다는 방식(교차검증판) | 18화 |
IN_SEQUENCE_LOAD 깃발이 켜지는 조건(혼자서는 안 켜진다 · 세 세션이면 켜진다 · 시퀀스를 안 불러도 켜진다) | 열어 둔다 |
← 16화 「처음으로 시스템 전체를 본다」 · 교재 전체 안내 · 다음 → 18화 「튜너가 되는 날」