테마
튜너의 1년 — 이 교재는 무엇이고, 어떻게 읽는가
오라클 SQL 튜닝을 시나리오로 배운다. 19개 화(話)가 하나로 이어진 이야기이고, 화마다 끝에 직접 만든 문서가 하나씩 남는다. 마지막 화에서는 후배를 맞고, 사내 튜닝 표준을 제정한다.
1. 왜 시나리오인가
기존 SQL 튜닝 교재는 대개 이렇게 쓰여 있다.
"인덱스 컬럼을 가공하면 인덱스를 사용할 수 없다. 아래 예를 보자.
WHERE TO_CHAR(dt,'YYYYMMDD') = '20240601'→ 이렇게 고친다 →WHERE dt >= ..."
맞는 말이다. 그런데 이 방식에는 세 가지가 빠져 있다.
| 빠진 것 | 그래서 생기는 일 |
|---|---|
| 누가 왜 그렇게 짰는가 | "이런 바보 같은 코드를 왜 짜지?"에서 학습이 멈춘다. 실제로는 다들 이유가 있어서 그렇게 짠다 |
| 얼마나 나쁜가 | "느리다"는 알겠는데 5,827인지 1,026,849인지 모른다. 크기 감각이 안 생긴다 |
| 고친 다음엔? | 하나 고치면 끝인 줄 안다. 실제로는 고친 자리에서 다음 문제가 생긴다 |
이 교재는 그 셋을 채운다.
- 누가 왜: 나쁜 코드마다 작성자의 커밋 로그와 회의록이 나온다. 대부분 그때는 옳았다.
- 얼마나: 모든 판단을 논리적 읽기(blocks) 숫자로 내린다. 그 숫자는 직접 잰다.
- 다음엔: 12화에서 내린 처방 때문에 13화에서 장애가 난다. 04화에서 만든 인덱스는 14화에서 청구서가 되어 돌아온다.
핵심 설계: 매주 새로운 예제를 보는 게 아니라, 같은 코드 6조각을 19주 동안 계속 고친다. 그래서 "이번 주에 배운 것"이 "지난주에 한 것" 위에 쌓인다.
2. 무대와 사람
회사
㈜한빛유통은 온라인 쇼핑몰이고, 통합주문시스템 이름은 HANORDER다. 주문 100만 건, 고객 2천 명, 상품 100종 규모이고, 야간에 정산 배치가 돈다.
주인공 '나'는 입사 3일째인 초보 튜너다. 첫 티켓을 받으면서 이야기가 시작된다.
세 사람
🕵️ 강도현: 2년 전에 퇴사한 선배
HANORDER를 처음 만든 사람이다. 지금 문제가 되는 코드는 전부 그가 짰다. 인수인계 문서는 없고, 소스 맨 위에 주석 한 줄만 남아 있다: -- 건드리지 말 것 (2019.03 강도현).
그는 악당이 아니다. 이 교재에서 가장 중요한 설계가 바로 이것이다.
화마다 그가 왜 그렇게 짰는지가 커밋 로그와 회의록, 감사 지적서로 밝혀진다.
2019-03-11 hd.kang "날짜 타입 비교에서 시분초 때문에 하루가 누락되는 버그 수정. 문자로 통일."→ 그는 실제 버그를 고친 것이다. 방법이 틀렸을 뿐이다.
2018-11-06 성능개선 회의 "등급별 집계 3초 → USE_NL 적용 후 0.4초. 적용 승인."→ 3초가 0.4초가 된 건 사실이었다. 그때 데이터는 2만 건이었다. 그런데 그때도 블록으로 쟀다면 결론이 같았을까? 06화에서 직접 잰다.
그래서 미워하게 되는 대상은 사람이 아니라 「근거를 남기지 않은 결정」이 된다. 이 깨달음은 18화에서 만들 표준의 마지막 조항이 된다.
🧭 윤세라: 새로 온 튜너 선배(경력 12년)
'나'의 멘토다. 절대 답을 먼저 주지 않는다. 대신 되묻는다.
"초로 말하지 마. 블록으로 말해.""네가 방금 뭐라고 생각했지?""그럼 대상이 전체의 절반이면?"
안전 수칙에는 결벽에 가깝다. 운영 DB는 건드리지 않고, 통계를 조작하면 반드시 원복하고, 인덱스는 INVISIBLE로 먼저 만든다.
👤 '나': 초보 튜너이자 독자 자신
화마다 먼저 틀린 가설을 낸다. 일부러 그렇게 짰다. 틀린 생각을 글로 적어 두어야, 나중에 무엇이 바뀌었는지 알 수 있다.
조연: 박팀장(운영, "지금 화면이 안 뜬다고요") · 조민우(정산 현업, "숫자가 회계와 안 맞아요") · 오과장(배치, 04:00 마감)
3. 관통 코드 6조각 — 19화를 꿰는 물건
강도현이 남긴 야간 정산 패키지 PKG_ORD_STLM 안에 조각이 6개 있다.
| ID | 무엇 | 처음 상태 | 몇 번 고치나 |
|---|---|---|---|
| Q1 | 기간별 주문 조회 | 날짜를 TO_CHAR로 문자 변환해 비교 | 3번(02화 rev01·rev02 · 03화 rev03). 04화에서는 더 고칠 게 없다는 것을 잰다 |
| Q2 | 고객등급별 매출 집계 | USE_NL 힌트 못박음 + 스칼라 서브쿼리 | 06·08화(07화에서는 재기만 한다) |
| Q3 | 상품평가 조인 | M:1:M 직접 조인 → 매출이 중복 합산됨 | 1번(08화) |
| Q4 | 정산 목록 페이징 | 전체 정렬 후 잘라내기 + 총건수 재조회 | 09·11화 |
| P1 | 정산 적재 배치 | 한 건씩 INSERT + 1,000건마다 커밋 | 2번(14화 rev01·rev02). 13화에서는 재기만 하고, 15화에서는 더 고칠 게 없다는 것을 잰다(같은 줄기의 주간 보정 배치는 15화 ADJ_rev01) |
| N1 | 채번 | MAX(번호)+1 | 1번(12화) |
리비전 규율: 조각마다 Q1_rev00.sql, Q1_rev01.sql처럼 번호를 붙인다. 화마다 처음 하는 일은 "직전 리비전을 열어 diff를 뜨는 것"이다.
왜 중요할까? "점점 나아진다"는 것을 느낌이 아니라 파일로 확인할 수 있기 때문이다. 18화에서
Q1_rev00.sql과Q1_rev03.sql을 나란히 놓으면 1년 동안 배운 것이 한눈에 보인다.
4. 19화의 흐름 — 어떤 이야기가 어떻게 이어지는가
19개 화는 주제를 그냥 늘어놓은 것이 아니다. 앞 화가 다음 화의 원인이 된다.
1막 · 인덱스를 배운다 (01~05화)
| 화 | 사건 | 배우는 것 |
|---|---|---|
| 01 | 인수인계 없는 인수인계: "정산이 30초" | 재는 법. 아무것도 고치지 않는다 |
| 02 | 인덱스는 있는데 안 탄다 | 인덱스를 못 타는 이유 6가지. 첫 성취 |
| 03 | 날짜가 문자로 박혀 있다 | 형변환이 인덱스를 원천 봉쇄하는 구조 |
| 04 | 인덱스를 태웠더니 더 느려졌다 | 클러스터링 팩터. 02화의 믿음이 뒤집힌다 |
| 05 | "지웠는데 왜 안 빨라지죠" | 블록·HWM. 여기서 만든 빚이 14화로 간다 |
1막의 반전: 02화에서 "인덱스를 만들면 빨라진다"를 배우고, 04화에서 그게 틀릴 수 있다는 걸 본다. 04화에는 행수와 컬럼, 인덱스 정의가 완전히 똑같은데 CF만 45배 차이 나는 두 테이블이 나온다(
ORDvsORD_RANDOM).
2막 · 조인과 옵티마이저를 배운다 (06~11화)
| 화 | 사건 | 배우는 것 |
|---|---|---|
| 06 | 100만 번의 왕복: 힌트의 유효기간 | 조인 3종. 회의록은 사실이었는데 왜 틀렸나 |
| 07 | 히트율 99퍼센트인데 서버가 탄다 | 버퍼 캐시 내부. "메모리라 빠르다"의 배신 |
| 08 | 금액이 50배로 찍힌다 | 쿼리 변환 + 정합성 사고 |
| 09 | 100페이지째부터 화면이 멈춘다 | 소트와 페이징 재설계 |
| 10 | 어제는 0.1초 오늘은 30초 | 통계와 히스토그램 |
| 11 | 같은 SQL이 수만 개 | 파싱·커서·바인드 변수 |
2막의 연쇄: 06화에서 힌트를 떼면 해시 조인이 되고, 그 때문에 메모리가 부족해져 09화의 TEMP 문제로 이어진다. 10화에서 넣은 히스토그램은 11화에서 바인드 변수와 정면충돌한다. 08화의 정합성 사고 이후, 모든 리포트에 회귀 검증 칸이 생긴다.
3막 · 동시성과 배치를 배운다 (12~15b화)
| 화 | 사건 | 배우는 것 |
|---|---|---|
| 12 | 아무도 안 건드렸는데 전체가 멈췄다 | Lock·무인덱스 FK. 첫 장애 대응 |
| 13 | 안전 조치의 대가 | Redo·Undo·ORA-01555 |
| 14 | 야간 배치가 아침을 넘긴다 | DML·파티션. 05화의 청구서 도착 |
| 15 | SQL은 다 고쳤는데 왜 느린가 | PL/SQL 컨텍스트 스위칭 |
| 15b | 같은 답을 두 번 계산하지 마라 | 캐싱 4계층 |
3막의 뼈아픈 연결: 12화에서 '나'는 "트랜잭션을 짧게 하세요"라는 재발방지책을 낸다. 13화의 장애는 바로 그 처방에서 나온다. 1,000건마다 커밋했더니
ORA-01555가 터진다. 그때 선배가 묻는다. "12화 포스트모템의 '리스크' 칸, 왜 비워 뒀어?"
4막 · 시스템 전체를 본다 (16~18화)
| 화 | 사건 | 배우는 것 |
|---|---|---|
| 16 | 처음으로 시스템 전체를 본다 | AWR. 부하를 직접 만들어 건다 |
| 17 | 평균은 거짓말을 한다 | ASH·ADDM. 16화 결론이 뒤집힌다 |
| 18 | 튜너가 되는 날 | 표준 제정 + 졸업 과제 |
4막의 도약: 15화에서 만든 PL/SQL 배치가 16화의 부하 발생기가 된다. 16화에서 1시간 평균으로 내린 결론은, 17화에서 ASH로 그 안의 5분을 열면 뒤집힌다(평균 5.5%였던 락 대기가 그 5분에는 100%였다). 18화 졸업 과제는 한 번도 본 적 없는 스키마(
ECOMMERCE)를 혼자 진단하는 것이다.
전체 인과 지도
02 Q1 살림 ──▶ 03 문자 날짜 ──▶ 04 인덱스가 배신함(CF)
│
04에서 만든 인덱스 ─┐
05 블록·HWM ──── 빚 ──────────┐ │
│ │
06 힌트 제거 → 해시 → 메모리 부족 ──▶ 09 TEMP로 회수
│ │
08 정합성 사고 ──▶ 이후 전 리포트에 회귀검증 필수
│ │
10 히스토그램 ──▶ 11 바인드와 충돌 │ │
11 처리량 증가 ──▶ 12 숨어 있던 락이 드러남
12 "트랜잭션을 짧게" ──▶ 13 그 처방이 ORA-01555를 만든다
│ │
14 배치 ◀── 05의 HWM 청구서 ──┘ │
◀── 04의 인덱스 적재 비용 청구서 ─┘
15 PL/SQL 완결 ──▶ 그 산출물이 16화의 부하 발생기
16 AWR 1시간 평균 ──▶ 17 ASH 5분으로 결론이 뒤집힘
01 베이스라인 ─────────────────▶ 18 전부 재측정해 나란히 놓는다5. 한 화는 어떻게 생겼는가
모든 화는 똑같은 파일 세 개와 폴더 세 개로 이루어져 있다. 파일마다 읽는 방식이 다르다.
| 파일 | 무엇을 하나 | 언제 펴나 | |
|---|---|---|---|
| 읽는다 | 1_사건.md | 그 주 월요일 하루가 소설처럼 흘러간다. 티켓이 오고, 주인공이 먼저 틀리고, 윤세라 선배가 되묻고, 재고, 고친다. 중간중간 "여기서 멈추고 직접 재 보자"는 중단 지점이 있다 | 처음에. 30~40분 |
| 한다 | 3_실습과_기록.md | 내 컨테이너에서 같은 숫자가 나오는지 STEP별로 잰다. 서술 답안 · 파인만 자가설명 · 그 화의 리포트 한 장을 여기서 쓴다 | 읽은 직후. 60~90분 |
| 찾는다 | 2_파고들기.md | 원리 · 측정 도구 · 표 · 원서와 공식문서 좌표. 이야기에서 지나간 것을 사전처럼 찾아보는 곳 | 막힐 때, 그리고 1년 뒤 "그게 뭐였지?" 할 때 |
| 폴더 | 무엇 |
|---|---|
실습/ | 그 화의 SQL 스크립트. 몇 번을 돌려도 같은 결과가 나오게 짜여 있다(맨 앞에서 상태를 되돌리고 시작한다) |
리비전/ | 관통 코드 6조각의 판(版). Q1_rev00 → rev01 → … 고친 것은 전부 여기 남는다 |
README.md | 그 화의 표지: 지난주에서 들고 오는 것 · 오늘의 전개 · 실습 준비 · 다음 화로 넘기는 것 |
1을 읽고 3을 따라 한다. 2는 막힐 때 펼친다.
예전 설계(한 화 = 11개 파일)는 01화의
_구버전_11섹션/*(공개 사이트에는 없음)*에 보관했다. 11섹션이 맡던 역할(사건, 현장 관측, 나쁜 SQL 해부, 내부 원리, 개선 사다리, 측정 증거, 선배의 한마디, 서술 답안, 파인만, 보고서, 공식문서)은 사라지지 않았다. 이야기 속으로 녹아들었을 뿐이다.
세 가지 레인 — 시간이 없는 주도 끊지 않는다
| 레인 | 시간 | 무엇을 하나 |
|---|---|---|
| 최소 통과 | 약 40분 | 1_사건을 중단 지점 1까지 → 3_실습의 핵심 STEP(각 화 README에 적혀 있다) |
| 표준 | 2~2.5시간 | 1_사건 전부 → 3_실습 전부(리포트 포함) |
| 심화 | +2시간 | 2_파고들기 정독 + 원서 해당 장 + 다른 고객사 컨테이너에서 교차 확인 |
최소 레인만 밟아도 리비전은 전진하고 원장에 한 줄이 남는다. 완주가 이 교재의 유일한 성공 조건이라, 바쁜 주를 위한 길을 처음부터 만들어 뒀다.
6. 시작하기
준비물 — 00 · 자료 준비
이 교재는 빈 PC에서 시작한다. Docker도, 오라클도, 데이터도 처음부터 만든다. 그 과정이 00화다.
→ 00 · 자료 준비 — 빈 PC → 첫 고객사 DB → Buffers 5,827 한 장 (Docker가 있으면 약 3분)
이미 만들어 두었다면:
bash
docker start ora-hanorder # 그리고 00_자료준비/고객사/L1_ora-hanorder/03_환경검증.sql 이 통과하면 된다이 교재는 컨테이너 4대를 쓴다. 컨테이너마다 다른 고객사 시스템이다(00_자료준비/고객사/).
| 컨테이너 | 무엇 | 언제 | 만드는 시점 |
|---|---|---|---|
ora-hanorder | ㈜한빛유통: 본편 무대 | 01~13, 15화 | 00화(지금) |
ora-dist | 본사 레거시 유통 시스템 | 03·09화 | 03화 전 |
ora-burger | 인수한 버거 프랜차이즈 POS | 04·14화 | 04화 전 |
ora-newshop | 신규 이커머스 + 졸업 과제 | 12·18화 | 12화 전 |
ora-hanorder하나만 있으면 01~13화를 진행할 수 있다. 나머지는 그때 만든다 →00_자료준비/03_다른_고객사_세_곳.md
첫 화로
→ 01화 · 인수인계 없는 인수인계
집필된 화(전부): 01화 · 02화 · 03화 · 04화 · 05화 · 06화 · 07화 · 08화 · 09화 · 10화 · 11화 · 12화 · 13화 · 14화 · 15화 · 15b화 · 16화 · 17화 · 18화
마지막 화에서 남긴 문서: 사내 튜닝 표준 T6 v1.0 · SQL 코딩 가이드 v1.0 · W01 베이스라인 원장 (01화 · 18화 2열) · 리비전 대장
7. 이 교재가 다루지 않는 것
정직하게 밝혀 둔다.
| 다룬다 | 개념만 | 다루지 않는다 |
|---|---|---|
| SQL 튜닝 전반, 인덱스·조인·옵티마이저·통계 | RMAN 백업 전략 | RMAN 실기 |
| 버퍼캐시·Redo/Undo·Lock 내부 | Data Guard | Data Guard 구성 |
| PL/SQL 성능 | RAC 개념 | RAC 구축 |
| AWR·ASH·ADDM·Statspack | Resource Manager | 업그레이드·패치, ASM |
실습은 Docker Free 하나로 모두 마친다. 병렬 실행, 수십 세션 부하,
dbca설치 같은 EE(GCP) 실습은 본편에서 다루지 않고, 필요성이 확인되면 별권으로 낸다.
겨냥하는 자격증은 1Z0-084 (Performance Management and Tuning) 하나다. 1Z0-082/083과 OCM의 운영 영역(백업·복구·RAC)은 이 교재의 범위가 아니다.
커리큘럼의 근거는 시험 코드가 아니라 오라클 공식 성능 진단 방법론(Performance Tuning Guide 3장 → 5장 → 6장 → 9장 → 7장 → 10장)을 그대로 따라가는 데 있다. 시험이 개편돼도 이 교재는 흔들리지 않는다.
8. 관련 문서
| 문서 | 무엇 |
|---|---|
_개정안/00_개정계획_v1.0.md (공개 사이트에는 없음) | 전체 설계도. 19화 마스터표·인과사슬·제작 계획 |
_막힘로그.md | 파인만 태그 누적 → 재학습 큐 |
40_보고서와_표준/ | 리포트 양식(T0~T9)과 직접 제출한 문서들 |
00_자료준비/ | 환경과 자료. 빈 PC 설치 · 고객사 컨테이너 4대 · 접속법 · 문제해결 · 원서/공식문서 지도 |
00_자료준비/원천지도/ | 성능고도화 목차 색인 · 오라클 공식문서 지도 |