Skip to content

10화 · 어제는 0.1초 오늘은 30초 ​

🗓️ 5월 6일 (월) · 09화에서 일주일 뒤 · 무대 ora-hanorder (+ 저녁에 ora-dist) 오늘 여는 것: 옵티마이저가 보는 숫자. SQL은 한 글자도 안 바뀌었는데 Q1과 Q2가 함께 무너진다. 그리고 01화 첫날 "고칠 수 있어. 근데 오늘은 아니야"라며 미뤄 둔 16배를 오늘 갚는다.


파일 세 개 ​

파일무엇을 하나시간
읽는다1_사건.md일요일 밤의 재적재를 복사본에서 그대로 되풀이하고, 구내식당 식수표를 보고, E-Rows를 손으로 계산하고, 히스토그램이 고치는 곳과 망치는 곳을 확인하는 하루40분
한다3_실습과_기록.md재적재 재현 · 적재 방식별 통계 · 손계산 · 히스토그램 4종 · 확장 통계 · 본사 문자 날짜 · 정책 적용 · 정책서 1·2호 · T1 8호80~110분
찾는다2_파고들기.md추적 SOP · 손계산 공식 · 히스토그램 4종 · 통계가 만들어지는 길 · out-of-range · 확장 통계 · 10053 · 피드백과 지시문 · 09화 정정필요할 때

1을 읽고 3을 따라 한다. 2는 막힐 때 펼친다. 시간이 없는 주에는 1_사건을 중단 지점 1까지 읽고 3_실습은 STEP 0, 1, 2, 4, 5, 8, 9만 한다(약 60분). STEP 8(정책 적용)은 건너뛰면 안 된다. 11화가 그 상태에서 시작한다.


지난 화에서 들고 오는 것 ​

Q1 = 91 · Q2 = 5,86902화와 03화의 커버링 인덱스, 06화의 힌트 제거로 얻은 값. 오늘 둘 다 무너진다
T_ORD_BIG 인덱스3개(IX_ORD_BIG_DT · IX_ORD_BIG_DT_AMT · IX_ORD_BIG_DT_SEQ). 09화에서 하나가 늘었다
T_ORD_BIG 통계선호도 FOR ALL COLUMNS SIZE 1 · 히스토그램 없음(00_자료준비의 04_상태복구.sql이 묶어 둔 상태)
01화가 미룬 것ORD_ST = 'CANCEL'에서 E-Rows 333K vs A-Rows 20,000, 16배
02 · 03 · 04 · 08 · 09화가 넘긴 것"E-Rows가 정확해도 틀리는 경우" · 문자 날짜의 선택도 · 조인 카디널리티 · 포스트잇 계획의 999K · 935페이지의 Cost

그리고 박팀장에게서 메시지가 하나 와 있다.

"등급별 매출 화면이 오늘 아침부터 30초 걸린답니다. 어제 당직이 볼 때는 0.1초였다고 합니다. 기간별 주문 조회도 느리답니다. 주말 사이 배포는 없었습니다."


오늘 여는 것 — 코드가 아니라 배치 스크립트의 한 줄 ​

-- 2019-09-02  hd.kang
--   적재 직후 통계 수집. 빼지 말 것.
--   자동 통계 수집 창이 TRUNCATE 직후의 빈 테이블을 수집한 적 있음 (월요일 오전 등급별 매출 지연)

【그때의 근거】 강도현은 2019년에 이미 이 일을 겪고 배치 맨 끝에 통계 수집 단계를 넣었다. 지난주 오과장은 그 단계를 뺐다. "인덱스가 세 개가 되고 적재가 길어져 04시를 넘긴다. 자동 통계 수집이 밤마다 돈다."라는 이유였다. 둘 다 맞는 말이었다. 자동 수집은 정말 밤마다 돈다. 빠진 것은 강도현이 적어 둔 "언제"였다.


오늘의 전개 — 네 번 틀리고, 정책을 쓴다 ​

내가 믿은 것측정
1차"운영 서버가 바쁜가 보다"코드 같음 · 연습 DB는 91 · 5,869 그대로. 운영 T_ORD_BIG 통계는 0행
—복사본에서 일요일 밤을 재현한다TRUNCATE → STALE → 빈 테이블 수집 → 100만 행 적재 → Q1 91 → 27,274 · Q2 5,863 → 1,026,852(E-Rows 1, 06화의 그 NL)
2차"0이면 이상하니까 세어 보면 되잖아"통계를 지우면 동적 샘플링 → 해시 5,869. 하지만 0이라고 적혀 있으면 그대로 믿는다
—응급 처치RESTORE_TABLE_STATS → Q2 5,868 · 재수집은 1초 남짓
3차"히스토그램을 전부 켜면 된다"FREQUENCY로 CANCEL이 333,333 → 20,000. 그러나 SIZE 2 HYBRID에서는 CANCEL이 1, TOP-FREQUENCY에서는 최솟값과 최댓값이 1
4차"히스토그램을 넣으면 더 정확해진다"CUS_ID와 함께 쓰면 167 → 10(실제 500)으로 더 틀린다. 컬럼 그룹은 그룹에도 히스토그램이 있어야 쓰인다
정책「통계 수집 정책서」 1 · 2호원본에 ORD_ST FREQUENCY · 6조각 계획 해시 변화 없음 · 배치에 수집 단계 복원

왜 이렇게 짰을까? 06화에서는 힌트를 지워 "판단을 옵티마이저에게 돌려줬다". 오늘은 그 판단의 재료(통계)가 0이 된다. 01화부터 끌고 온 16배를 고치는 도구(히스토그램)가 어디서 독이 되는지까지 본다. 그래서 오늘의 산출물은 처방이 아니라 정책이다.


오늘 배우는 한 문장 ​

옵티마이저는 데이터가 아니라 통계를 보고 계획을 세운다. 그리고 통계는 열마다 따로 있다.E-Rows는 산수다. 손으로 계산한 값과 맞는다면, 틀린 것은 옵티마이저가 아니라 통계나 가정이다. 판단을 옵티마이저에게 돌려줬다면, 옵티마이저가 보는 숫자를 누가, 언제, 어떻게 만드는지도 문서로 남겨야 한다.

E-Rows손계산어디서 봤나
9131,000,000 / 1,095하루
28,3361,000,000 × (30/1,094 + 1/1,095)02화 rev01
2,500 · 10,0001,000,000 × 0.25% · 1% (가공한 열의 고정값)02화 rev00 · 03화
333,3331,000,000 × 1/301화의 16배
10,000,000200,000 × 5,000 / 10008화의 50배
999,990ROWNUM을 떼고 본 포스트잇 조건09화의 999K
5,177문자 날짜를 수직선 위의 숫자로 본 값03 · 04화

🔑 오늘의 비유 — 구내식당 식수표 ​

월요일 점심, 한식 줄은 반찬이 떨어졌는데 샐러드 바에는 샐러드가 산더미다. 벽의 화이트보드에는 "오늘의 식수 300명 · 메뉴 3종"이라고 적혀 있다. 영양사: "식수표대로 100인분씩 했어요. 메뉴별 신청은 안 받거든요." 선배: "옵티마이저도 영양사야. 사람을 보고 밥을 짓는 게 아니라 식수표를 보고 지어."

구내식당오라클
식수표 (전날 집계 · 총인원과 메뉴 수)통계: NUM_ROWS · NUM_DISTINCT
영양사의 조리 · 배식 계획실행계획
실제로 온 사람데이터: A-Rows
"메뉴 3종이니 100인분씩"히스토그램 없는 등치 조건: 1 / NDV(01화의 333K)
메뉴별로 지난주에 나간 그릇 수FREQUENCY 히스토그램
일요일 밤 빈 사무실에서 센 월요일 식수표적재 도중 빈 테이블에서 모은 통계(NUM_ROWS 0)
'3층 30명'과 '샐러드 2%'를 따로 곱해 나온 3층 샐러드 0.6그릇(3층은 다이어트 팀)상관 컬럼 AND: 독립 가정
한 명씩 주문받기 / 뷔페 라인NESTED LOOPS / HASH JOIN (← 06화 4층 카드함)

(01화 커피 캐리어 = 블록 · 02화 출입대장 = 인덱스 · 06화 4층 카드함 = 조인 · 07화 탕비실 픽업 선반 = 버퍼 캐시 · 08화 회식 정산서 = 조인 곱셈 · 09화 출입대장 포스트잇 = 키 기반 페이징)


☠️ 오늘의 함정 — 히스토그램을 넣으면 더 정확해진다 ​

01화의 16배는 FREQUENCY 히스토그램 한 줄로 사라진다. 333,333 → 20,000. 그래서 "다 켜자"는 말이 나오기 쉽다.

ORD_ST에 히스토그램을 넣으면히스토그램 없음있음실제
ORD_ST = 'CANCEL'333,33320,00020,000
6월 AND CANCEL9,445567549
CUS_ID = 'CUS_000001' AND CANCEL16710 ← 더 틀린다500
같은 열에 버킷 2개(HYBRID)로 만들면 CANCEL은333,333120,000

이 데이터는 고객마다 상태가 하나뿐이다. 히스토그램에는 한 열의 분포만 담길 뿐, 열과 열 사이의 관계는 담기지 않는다. 그래서 옵티마이저는 두 선택도를 곱하기만 한다.

처방에는 리스크 칸이 있다. 정책서 1호의 4번 칸에 이 10을 적는다. "이 히스토그램은 'CANCEL'이 글자로 박혀 있을 때의 이야기"라는 점도 함께 적는다. 바인드 변수는 11화에서 다룬다.


실습 준비 ​

bash
docker start ora-hanorder
docker start ora-dist          # STEP 7 만

09화를 마친 상태(T_ORD_BIG 인덱스 3개 · 선호도 SIZE 1 · 히스토그램 없음)에서 시작한다. ora-dist는 03화를 마친 상태다.

스크립트컨테이너무엇걸리는 시간
실습/00_어제와_오늘.sqlora-hanorder재적재 사고 재현 · 기각할 대안(힌트) · 정합성 · 통계 이력 · RESTORE · 재수집30초~1분
실습/01_적재방식과_통계.sqlora-hanorder23ai 온라인 통계 · 동적 샘플링 · 1만 행 · NL로 넘어가는 문턱 · 최댓값 밖약 1분
실습/02_손계산.sqlora-hanorder등치 · 범위 · 고정값 · 조인 · out-of-range · Cost · 09화 999K · 935페이지 · 추적 SOP약 5초
실습/03_히스토그램_4종.sqlora-hanorderFREQUENCY · HYBRID · HEIGHT BALANCED · 상관 AND · Statistics Feedback · 컬럼 그룹 · 표현식 통계약 10초
실습/04_본사_문자날짜.sqlora-dist문자 날짜의 수직선 · HYBRID · 표현식 통계 · 실제 상관 둘 · TOP-FREQUENCY약 15초
실습/05_정책_적용.sqlora-hanorder★ 정책서 1호를 원본에 건다 · 6조각 전후 대조 · 종료 상태 확인약 10초
실습/90_적재_일반INSERT.sqlora-hanorder(선택) 운영과 똑같이 인덱스 3개 + 일반 INSERT 100만 행약 9분 (겹치면 20분 넘게)
실습/91_10053_비용검산.sqlora-hanorder(선택) 10053으로 손계산 · 정렬 비용 검산약 2초
실습/92_SPD_SYSDBA.sqlora-hanorder (SYS)(선택) SQL Plan Directive: 쌓이기는 하지만 기본 설정에서는 쓰이지 않는다약 1분 30초

모든 스크립트는 맨 앞에서 초기화하고, 실험은 복사본(*_C10)에서 한 뒤 끝에서 지운다. 그래서 몇 번을 돌려도 결과가 같다. 원본에 손대는 것은 05_정책_적용.sql 하나뿐이고, 그마저도 통계 선호도와 통계만 바꾼다(데이터와 인덱스는 그대로). ⚠️ 05를 마지막으로 돌린 상태로 끝낸다. 01~09화의 스크립트나 00_자료준비/.../04_상태복구.sql을 돌리면 SIZE 1로 다시 수집해 히스토그램이 사라진다. 그때는 05를 다시 돌린다.

ℹ️ 이 교재는 Docker로 띄운 Oracle Database Free 한 대에서 모든 실습을 마친다. 운영에서 '자동 통계 수집이 적재 도중에 도는' 상황은 시각을 맞춰 기다릴 수 없으므로, 자동 수집이 하는 일(선호도대로, 인덱스까지 수집)을 손으로 대신해 재현한다. 일요일 창의 시각(06:00부터 20시간)은 실제 설정을 조회해 확인한다. 운영의 재적재(일반 INSERT)는 같은 통계 상태를 만드는 힌트(APPEND NO_GATHER_OPTIMIZER_STATISTICS)로 빠르게 재현하고, 운영과 똑같은 재현은 90에 따로 두었다.


이 화의 산출물 ​

무엇어디
「통계 수집 정책서」 ★ 새 표준 문서. 1호 T_ORD_BIG(ORD_ST FREQUENCY) · 2호 재적재 배치40_보고서와_표준/템플릿/통계수집_정책서.md
E-Rows vs A-Rows 추적 SOP: 계획이 이상할 때 처음 30분 동안 할 일2_파고들기.md §1 · 정책서 양식 안
T1 리포트 8호: 처음으로 리비전이 없는 리포트. 대상은 SQL이 아니라 통계와 정합성 체크리스트 A~H3_실습과_기록.md STEP 9
리비전없음. 오늘은 SQL을 한 글자도 고치지 않았다. Q1 rev03 · Q2 rev01 · Q3 rev01 · Q4 rev02 그대로
09화 기록 정정935페이지 정렬 쪽 Cost는 'CPU'가 아니라 대부분 IO(TEMP를 쓸 것이라는 계산, 7,740) → 2_파고들기.md §11

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

무엇어디로
T_ORD_BIG.ORD_ST의 FREQUENCY 히스토그램(정책서 1호, 원본에 걸려 있다): 그 열을 바인드 변수로 받으면11화: 바인드 피킹과 히스토그램
인덱스 3개를 단 채 일반 INSERT 100만 행이 오래 걸리는 이유(오과장의 04시와 통계 수집 단계가 부딪친 뿌리)14화: 청구서
본사 CF10의 정책서 후보(문자 날짜, 매장/직원 상관). 유통지원팀 회신 대기회신이 오면 다시 잰다
정책서 1 · 2호의 재검토 조건(ORD_ST 새 값 · 적재 방식 변경 · 바인드)조건이 걸리면 다시 잰다 · 18화 원장 재측정

← 09화 「100페이지째부터 화면이 멈춘다」 · 교재 전체 안내 · 다음 → 11화 「같은 SQL이 수만 개」