테마
02화 · 인덱스는 있는데 안 탄다
🗓️ 3월 11일 (월) · 01화에서 일주일 뒤 · 무대
ora-hanorder오늘 배울 것: 인덱스는 정렬된 목록이다. 그리고 오늘은 두 번 틀린다.
파일 세 개
| 파일 | 무엇을 하나 | 시간 | |
|---|---|---|---|
| 읽는다 | 1_사건.md | 인덱스를 만들었는데 안 타고, 고쳤는데 또 안 타다가, 결국 64배 빨라지는 하루 | 30분 |
| 한다 | 3_실습과_기록.md | 6단계 실험 · 무력화 6종 · T1 리포트 1호 | 60~90분 |
| 찾는다 | 2_파고들기.md | B*Tree · 스캔 6종 · Sargable 교정표 · 커버링 · CF 맛보기 | 필요할 때 |
1을 읽고 3을 따라 한다. 2는 막힐 때 펼친다. 시간이 없는 주에는 1_사건을 중단 지점 1까지 읽고 3_실습은 STEP 0, 1, 2, 5만 한다(약 40분).
지난 화에서 들고 오는 것
Q1 = 5,827블록 | 6월 주문 27,390건을 세려고 테이블 전체를 읽는다 |
T_ORD_BIG 인덱스 | 0개 |
| 지난주 내 가설 | "100만 건인데 인덱스가 0개라서 느리다. 만들면 된다" |
그리고 박팀장에게서 메시지가 하나 와 있다.
"지난주 보고서 잘 봤습니다. 그런데 결론이 '아직 고치지 않았습니다'로 되어 있던데, 그래서 언제쯤 빨라지나요?"
오늘의 전개 — 두 번 틀리고 한 번 이긴다
| 한 일 | 결과 | |
|---|---|---|
| 1차 | ORD_DT에 인덱스를 만들었다 | 5,826. 그대로. 힌트를 줘도 안 탄다 |
| 2차 | TO_CHAR 가공을 없앴다(rev01) | 5,827. 그대로. 그런데 이번에는 오라클이 옳다 |
| — | 억지로 인덱스를 태워 봤다 | 27,466. 4.7배 느려졌다 |
| 3차 | 테이블을 읽을 이유를 없앴다(rev02) | 91. 64배. |
왜 두 번이나 틀리게 짰을까? "인덱스를 만들면 빨라진다"는 믿음은 한 번은 깨져야 하기 때문이다. 오늘 깨지지 않으면 04화에서 더 크게 깨진다.
오늘 배우는 한 문장
인덱스는 빨리 찾는 마법이 아니라 정렬된 목록이다. 조건이 정렬된 순서에서 "여기부터 여기까지"를 지목할 수 있으면
access, 지목하지 못하면filter다.
무력화 6종(컬럼 가공, 묵시적 형변환, 선두 %LIKE, 부정형, IS NULL, 선두 컬럼 누락)도 모두 이 한 문장으로 설명할 수 있다.
🔑 오늘의 비유 — 방문객 출입대장
안내 데스크 캐비닛에 종이 바인더로 된 출입대장이 있다. 방문 일시 순으로 적혀 있어서 6월 방문자를 세려면 6월 1일 첫 줄을 찾아 7월 1일이 나오기 전까지만 보면 된다. 1월부터 5월까지는 펴 볼 필요도 없다. 그런데 "날짜를 여덟 자리 숫자로 바꿔서 세라"라고 하면 사정이 달라진다. 대장에는 20240601이라고 적힌 줄이 없으니, 첫 줄부터 한 줄씩 바꿔 봐야 한다.
| 방문객 출입대장 | 오라클 |
|---|---|
| 방문 일시 순으로 적힌 바인더 | 인덱스: 키 순서로 정렬된 목록(IX_ORD_BIG_DT) |
| 6월 1일 첫 줄을 찾아 7월 1일 전까지 | INDEX RANGE SCAN: 조건이 access로 범위를 지목한다 |
| 1월~5월은 펴 보지 않는다 | 읽지 않은 블록만큼 Buffers가 줄어든다 |
| "여덟 자리 숫자로 바꿔서 세라" | 컬럼 가공 TO_CHAR(ORD_DT): 대장(인덱스)에는 그런 모양의 값이 없다 |
| 첫 줄부터 한 줄씩 바꿔 보며 세기 | filter: 다 읽고 버린다. 인덱스가 있어도 FULL로 읽는다 |
01화의 비유도 다시 나온다. 커피 캐리어(블록, 한 캐리어에 169건)다.
☠️ 오늘의 함정 — 빠른데 틀린 코드
TO_CHAR를 없애려다 보면 이렇게 쓰고 싶어진다.
sql
WHERE ORD_DT BETWEEN DATE '2024-06-01' AND DATE '2024-06-30'T_ORD_BIG에서 돌려 보면 결과가 똑같다. 테스트도 통과한다. 그런데 옆 테이블 T_ORD에 같은 코드를 쓰면 137건, 34,159,700원이 사라진다.
반면 강도현의 TO_CHAR 원본은 T_ORD에서도 정확하다. 강도현의 코드는 느릴 뿐, 답은 맞다.
느린 코드를 빠르게 만드는 것이 튜닝이고, 맞는 코드를 틀리게 만드는 것은 장애다.
실습 준비
bash
docker start ora-hanorder01화를 마친 상태(T_ORD_BIG에 인덱스 0개)에서 시작한다. 실습 스크립트는 맨 앞에서 인덱스를 지우고 시작하므로 몇 번을 돌려도 결과가 같다.
| 스크립트 | 무엇 |
|---|---|
실습/00_인덱스_실험.sql | 6단계 [A]~[F]. 오늘 실습의 본체 |
실습/01_무력화_6종.sql | 무력화 패턴과 교정형을 나란히 측정 |
실습/02_경계값_검증.sql | 회귀 검증. 빠르게 고친 뒤에도 답이 그대로인가 |
리비전은 리비전/에 Q1_rev00 → rev01 → rev02 순서로 남는다.
이 화의 산출물
| 무엇 | 어디 |
|---|---|
T1 단건 SQL 튜닝 리포트 1호 ★ (Q1 rev00 → rev02 · Buffers 5,827 → 91) | 3_실습 STEP 5 · T1 양식 |
Q1_rev00 → rev01 → rev02: 커버링 인덱스 (ORD_DT, ORD_AMT)까지 | 리비전/ |
이 화가 다음으로 넘기는 것
| 무엇 | 어디로 |
|---|---|
| CF = 1,000,000 (27,466의 정체) | 04화: 커피 캐리어가 다시 나온다 |
| 손익분기점은 몇 %인가 | 04화: 오늘 만든 인덱스로 범위를 넓혀 간다 |
| 인덱스 2개의 적재 비용 | 14화: 청구서가 날아온다 |
| 컬럼 가공을 지울 수 없을 때 | 03화: ora-dist는 날짜가 VARCHAR2(8)이다 |
E-Rows가 정확해도 틀리는 경우 | 10화: 히스토그램 |
← 01화 「인수인계 없는 인수인계」 · 교재 전체 안내 · 다음 → 03화 「날짜가 문자로 박혀 있다」