Skip to content

06화 · 파고들기 ​

필요할 때 찾아보는 문서다. 처음 읽을 때는 1_사건.md만으로 충분하다.

찾는 것바로 가기
조인 3종을 한 문장으로§1 세 가지 답
Starts로 알 수 있는 것§2 Starts
NL의 총일량은 어떻게 계산하나§3 NL
해시 조인의 build / probe§4 해시
손익분기점을 직접 재는 법§5 손익분기점
PGA · work area 3등급 · Used-Tmp§6 work area
힌트는 무엇을 하는가 · 왜 늙는가§7 힌트
「힌트 유효기간 대장」 양식§8 대장
측정값 전부§9 측정
공식문서 좌표§10 좌표

1. 조인 3종 — 같은 질문의 세 가지 답 ​

조인 대상은 언제나 두 집합이다. 먼저 읽는 쪽이 outer(선행), 거기에 맞춰 찾는 쪽이 inner(후행)다. 세 방식은 모두 한 질문에 답하는 방법이다.

바깥 한 건에 맞는 안쪽을, 어디서 찾을 것인가?

방식안쪽을 어디서 찾나안쪽에 필요한 것비용의 성질
NL (Nested Loops)매번 원래 자리로 가서조인 컬럼 인덱스outer 건수 N에 비례
해시 (Hash Join)PGA에 복사해 둔 해시 테이블에서없음. 대신 메모리양쪽을 한 번씩 읽는 값
소트 머지 (Sort Merge)양쪽을 줄 세워 나란히 놓고없음. 대신 정렬 공간양쪽 정렬 비용 + 한 번씩 훑기

결정적 차이 — 무엇이 N에 비례하는가 ​

NL        :  outer 스캔  +  N × (inner 한 건 찾는 비용)     ← N 이 곱해진다
해시      :  outer 스캔  +  inner 스캔 + 해시 테이블 적재   ← N 이 곱해지지 않는다
소트 머지 :  outer 스캔  +  inner 스캔 + 양쪽 정렬          ← N 이 곱해지지 않는다

06화의 핵심은 이 한 줄이다. NL만 N이 곱해진다. 그래서 데이터가 늘어나면 NL 비용만 함께 커진다.

등치(=) 조건이 아닐 때 ​

해시 조인은 등치 조건에서만 쓸 수 있다. 해시 함수는 "같은 값"끼리만 같은 자리로 보내기 때문이다. BETWEEN, >, < 같은 비등치 조인은 NL이나 소트 머지로만 처리할 수 있다. (이 교재에서는 선분이력을 다룰 때 비등치 조인을 만난다.)


2. Starts — 왕복 횟수 ​

DISPLAY_CURSOR(FORMAT=>'ALLSTATS LAST')의 첫 열로, 그 단계가 몇 번 실행됐는지를 보여 준다.

자리정상값1이 아니면
outer 쪽(NL의 위쪽 자식)1그 위에 또 조인이 있다
inner 쪽(NL의 아래쪽 자식)outer의 A-Rows와 같다그게 왕복 횟수다
스칼라 서브쿼리보통 값의 종류 수캐시가 안 먹으면 행 수만큼 뛴다(→ 07화)
SORT JOIN(소트 머지의 아래쪽)조인 키 그룹 수왕복이 아니라 되감기다(싸다)

06화 실측 ​

xplan
|   4 |   NESTED LOOPS                 |           |      1 |   1000K|  ...  |
|   6 |     TABLE ACCESS FULL          | T_ORD_BIG |      1 |   1000K|  ...  |   ← outer, Starts 1
|*  7 |     INDEX UNIQUE SCAN          | PK_M_CUS  |   1000K|   1000K|  ...  |   ← inner, Starts 1,000,000
|   8 |    TABLE ACCESS BY INDEX ROWID | M_CUS     |   1000K|   1000K|  ...  |   ← inner, Starts 1,000,000

Starts와 A-Rows를 나란히 놓고 보자. outer가 100만 행을 내보냈고, inner가 정확히 100만 번 열렸다.

📌 Starts는 인덱스로 줄지 않는다 ​

06화에서 M_CUS에 커버링 인덱스를 만들어 봤다. Buffers는 1,026,849 → 36,852로 28배 줄었다. 그런데 Starts는 1,000,000 그대로였다.

인덱스는 왕복 한 번에 드는 비용을 줄인다. 왕복 횟수는 조인 방식을 바꿔야만 줄어든다.


3. NL 조인의 총일량 ​

총일량 = outer 스캔 비용  +  N × (inner 한 건을 찾는 비용)

06화 실측 분해 (Q2 rev00) ​

자리오퍼레이션StartsBuffers건당
outerTABLE ACCESS FULL T_ORD_BIG15,826—
inner ① 주소 찾기INDEX UNIQUE SCAN PK_M_CUS1,000,00021,0110.021
inner ② 본문 읽기TABLE ACCESS BY INDEX ROWID M_CUS1,000,0001,000,0001.000
스칼라 서브쿼리M_RGN812—
1,026,849

inner 한 건을 찾는 데 약 1.02블록이 든다. 그 자체는 싸다. 비싼 이유는 그 1.02를 100만 번 곱한다는 데 있다.

⚠️ "한 건에 4~5블록"이라는 흔한 설명 ​

교과서는 보통 "루트 → 브랜치 → 리프 → 테이블"로 세어 건당 4~5블록이라고 설명한다. 실측값은 1.02였다. 이유는 두 가지다.

  • PK_M_CUS가 BLEVEL 1 · LEAF_BLOCKS 6으로 아주 작다. 인덱스 전체가 7블록이다
  • 실행계획에 NESTED LOOPS가 두 겹으로 나타나 있다. 오라클의 NL 조인 배치(batching)다. inner 쪽 탐색을 모아서 처리하므로, 같은 블록을 연달아 읽을 때 논리적 읽기가 한 번으로 묶인다

그래서 건당 비용은 외우는 값이 아니라 재는 값이다. 다만 N이 곱해진다는 성질은 언제나 같다.


4. 해시 조인의 build와 probe ​

[1] build  : 작은 쪽을 한 번 읽어, 조인 키에 해시 함수를 걸어
             PGA 안의 해시 테이블(버킷 배열)에 올린다
[2] probe  : 큰 쪽을 흘려보내며, 각 행의 조인 키에 같은 해시 함수를 걸어
             해당 버킷만 뒤진다

해시 함수는 같은 값을 반드시 같은 버킷으로 보낸다. 그래서 한 버킷만 보면 된다. (다른 값이 같은 버킷에 들어올 수는 있어서, 버킷 안에서는 실제 값을 다시 비교한다.)

어느 쪽이 build가 되는가 — 실측으로 확인 ​

LEADING에 적은 첫 테이블이 build가 된다. 06화에서 두 방향을 다 재 봤다.

힌트buildBuffersOMemUsed-Mem
LEADING(c o) USE_HASH(o)M_CUS 2,000행5,8681,423K1,679K (0)
LEADING(o c) USE_HASH(c)T_ORD_BIG 100만 행5,86860M77M (0)

📌 build를 잘못 잡으면 블록이 아니라 메모리에서 드러난다 ​

Buffers는 5,868로 똑같다. 두 실행계획의 차이는 쓴 메모리가 45배라는 것 하나뿐이다. 그리고 메모리가 모자라는 순간 이 차이가 시간으로 터진다(§6).

작은 쪽을 build로 잡는다. 통계가 정확하면 옵티마이저가 알아서 그렇게 한다.

메모리에 다 못 올라가면 — Grace 분할 ​

build 쪽이 work area보다 크면 오라클은 양쪽을 같은 해시 함수로 파티션해서 TEMP에 나눠 쓰고, 짝이 맞는 파티션끼리 하나씩 올려 조인한다. 실행계획에 Used-Tmp가 나타나는 게 바로 이때다. 자세한 내용은 §6에, 깊은 이야기는 09화에 있다.


5. 손익분기점을 직접 재는 법 ​

NL이 이기는 구간은 분명히 있다. 외울 숫자는 없지만 재는 방법은 있다.

두 식을 세운다 ​

NL   비용 ≈ outer 스캔 + N × (inner 건당 비용)      ← 기울기가 있다
해시 비용 ≈ outer 스캔 + inner 스캔 + 적재          ← N 과 무관한 상수

두 식이 만나는 N이 손익분기점이다.

06화 실측 (T_ORD_BIG × M_CUS · 실습/01) ​

OUTER_ROWS NL_BUFFERS HJ_BUFFERS VERDICT    NL_OVER_HJ
        10         25         36 NL 승              .7
        20         35         36 NL 승               1
        25         40         36 HASH 승           1.1
       100        115         36 HASH 승           3.2
      2000       2088         45 HASH 승          46.4
     20000      20798        151 HASH 승         137.7
    200000     211665       1229 HASH 승         172.2
   1000000    1026837       5856 HASH 승         175.3

작은 N 구간에서 두 식을 읽어 내면 이렇다.

NL   = 15 + N        (6 블록 outer + 9 블록 인덱스 + N)
해시 = 36            (30 블록 M_CUS + 6 블록 outer)

15 + N = 36   →   N = 21

손익분기점은 outer 21건이다. 그 아래로 20건까지는 NL이 이긴다.

⚠️ 21은 이 조인에만 해당한다 ​

M_CUS를 한 번 훑는 데 30블록이 들어서 나온 값이다. inner가 커지면 분기점은 뒤로 밀린다. 분기점은 "inner를 한 번 읽는 값"으로 정해진다.

inner가해시의 상수항이분기점이
작다(코드 테이블 수십 블록)작다앞으로 당겨진다(NL 구간이 좁다)
크다(수십만 블록)크다뒤로 밀린다(NL 구간이 넓어진다)

그래서 "NL은 소량, 해시는 대량"이라는 말은 절반만 맞다. 정확히 말하면 "outer 건수 × inner 건당 비용"과 "inner 전체를 한 번 읽는 값"을 비교하는 문제다.

2018년 회의록이 빠뜨린 것 — 실측으로 확인 ​

2018년 (2만 건)지금 (100만 건)
NL20,7981,026,849
해시1515,869
NL이 진 배수137.7배175.0배
NL의 손해(절대량)약 2만 블록 ≈ 10밀리초(12.6ms vs 2.4ms)약 102만 블록 ≈ 0.5초

📌 이 표가 06화에서 가장 중요하다 ​

2018년에도 NL은 지고 있었다. 약 138배 차이였다. 달라진 것은 틀린 대가의 크기다. 10밀리초는 아무도 못 느끼지만 0.5초는 민원이 된다.

그래서 정확하게 말하면 이렇다. 힌트의 유효기간이 지난 것이 아니라, 처음부터 틀린 힌트의 대가가 커진 것이다. 앞의 해석은 "그때는 옳았으니 어쩔 수 없다"로 끝나고, 뒤의 해석은 "주기적으로 다시 재야 한다"로 이어진다.


6. PGA와 work area 3등급 ​

PGA는 내 책상이다 ​

SGA의 버퍼 캐시PGA
누가 쓰나모든 세션이 공유그 서버 프로세스 전용
보호 장치래치가 필요하다없다
무엇이 올라가나데이터 블록정렬·해시·비트맵 작업 공간

같은 데이터라도 PGA에서 다루는 쪽이 싸다. 공유하지 않으니 잠글 필요가 없기 때문이다.(래치와 버퍼 캐시의 내부는 07화에서 다룬다.)

work area는 PGA 안에서 오퍼레이션 하나(해시 조인 하나, 정렬 하나)에 배정되는 공간이다. WORKAREA_SIZE_POLICY=AUTO이면 오라클이 PGA_AGGREGATE_TARGET 범위 안에서 알아서 나눠 준다.

3등급 — 실행계획에서 읽는 법 ​

| ... |  OMem |  1Mem | Used-Mem | Used-Tmp|
| ... |  1423K|  1423K| 1679K (0)|         |
                       ^^^^^  ^
                       실제   패스 수
등급괄호무슨 일이 있었나Used-Tmp
optimal(0)전부 메모리에서 끝났다없음
onepass(1)TEMP에 한 번 내렸다가 한 번 올렸다있음
multipass(N)내렸다 올리기를 N번 반복했다있음. 많다
열뜻
OMemoptimal로 끝내려면 필요하다고 추정한 크기
1Memonepass로라도 끝내려면 필요한 크기
Used-Mem실제로 쓴 메모리 + (패스 수)
Used-TmpTEMP에 내려놓은 양

V$SQL_WORKAREA — 계획 밖에서 같은 사실 확인 ​

sql
SELECT w.operation_id, w.operation_type, w.policy,
       w.estimated_optimal_size, w.estimated_onepass_size,
       w.last_memory_used, w.last_execution, w.last_tempseg_size
  FROM v$sql_workarea w WHERE w.sql_id = '&sql_id';

06화 실측값이다(세 등급이 모두 나온다).

TAG  OP_ID OPERATION_TYPE  POLICY     EST_OPT     EST_1P   LAST_MEM LAST_EXEC    LAST_TEMP
---- ----- --------------- ------- ---------- ---------- ---------- ----------- ----------
W1       4 HASH-JOIN       AUTO       1457152    1457152    1703936 OPTIMAL
W2       4 HASH-JOIN       MANUAL    63507456    8785920     260096 4 PASSES      25165824
W3       5 SORT (v2)       MANUAL       73728      73728      66560 1 PASS         1048576
W3       7 SORT (v2)       MANUAL    27657216    1907712      66560 100 PASSES    26214400

☠️ 블록은 그대로인데 시간만 터진다 ​

BuffersReadsWrites시간
W1 optimal5,869000.17초
W2 4패스5,86811,7842,9525~8초

일량(Buffers)은 한 블록도 늘지 않았다. 일하는 장소가 메모리에서 디스크로 바뀌었을 뿐이다.

(W2의 시간은 실행할 때마다 5~8초 사이를 오간다. 그래도 Buffers가 5,868에서 꿈쩍도 하지 않는다는 사실은 그대로다.)

📌 T0 측정규약의 첫 사각지대 ​

01화에서 정한 T0은 "Buffers로 말한다"이고, 그 규약은 옳다. 다만 이 경로 하나는 Buffers에 찍히지 않는다. 해시 조인이나 정렬이 있는 실행계획에서는 Used-Mem / Used-Tmp / Reads / Writes를 함께 본다. 본격적인 이야기는 09화에서 한다.

재현 방법 (Free 컨테이너 안에서) ​

메모리 등급이 뒤집히는 현상은 세션 파라미터만으로 재현한다. 인스턴스는 건드리지 않는다.

sql
ALTER SESSION SET workarea_size_policy = MANUAL;
ALTER SESSION SET hash_area_size = 65536;   -- 64KB
ALTER SESSION SET sort_area_size  = 65536;
-- ... 측정 ...
ALTER SESSION SET workarea_size_policy = AUTO;   -- 반드시 원복

이 교재의 실습은 Docker로 띄운 Oracle Database Free 한 대에서 모두 끝난다. 서버 메모리 자체를 바꿔 가며 대규모 EE 환경과 비교하는 일은 이 교재에서 다루지 않는다. 다루더라도 별권에서 다룬다. 여기서처럼 세션 단위로 메모리를 줄이는 것이 같은 현상을 같은 컨테이너에서 재현하는 정식 방법이다.


7. 힌트는 무엇을 하는가 ​

힌트는 "지시"가 아니라 "선택지 제한"이다 ​

옵티마이저는 가능한 실행계획 후보를 만들어 놓고 그중 비용이 가장 싼 것을 고른다. 힌트는 그 후보 목록에서 나머지를 지워 버린다. 남은 것 중에서 고르니 "강제"처럼 보일 뿐이다.

힌트지우는 것
LEADING(o c)o가 선행이 아닌 모든 조인 순서
USE_NL(c)c를 NL이 아닌 방식으로 붙이는 모든 계획
USE_HASH(c)c를 해시가 아닌 방식으로 붙이는 모든 계획
USE_MERGE(c)c를 소트 머지가 아닌 방식으로 붙이는 모든 계획

그래서 힌트는 옳고 그름을 정하는 게 아니라 "그 시점의 데이터에서 가장 싼 선택"을 못 박아 두는 일이다. 데이터가 바뀌면 그 선택은 저절로 낡는다. 그런데 힌트는 스스로 바뀌지 않는다. 코드에 그대로 박혀 있다.

02화와의 차이 — 이번엔 힌트가 먹었다 ​

02화06화
힌트INDEX(o IX_ORD_BIG_DT)LEADING(o c) USE_NL(c)
결과조용히 무시됨(컬럼이 가공돼 쓸 수가 없었다)그대로 적용됨
교훈오라클은 못 따를 힌트를 말없이 버린다오라클은 따를 수 있는 힌트는 끝까지 따른다

힌트가 무시되는 것보다 무서운 건, 몇 해 동안 충실히 지켜지는 것이다.

힌트를 떼기 전에 확인할 것 ​

sql
-- ① 힌트를 명시한 계획과 힌트 없는 계획의 Plan hash value 를 비교한다
--    같으면: 옵티마이저는 이미 같은 결론이다. 힌트는 아무것도 안 하고 있다
--    다르면: 옵티마이저가 다른 선택을 한다. 양쪽을 다 재고 판단한다
SELECT sql_id, plan_hash_value, buffer_gets, executions FROM v$sql WHERE ...;

-- ② 결과가 같은지 반드시 확인한다 (건수·합계)
-- ③ 환경 의존성을 적는다 (해시 조인이면 PGA)

06화에서 재 보니 힌트 없는 실행계획과 USE_HASH를 명시한 실행계획의 plan_hash_value가 둘 다 2292855265였다. 옵티마이저는 그 몇 해 내내 같은 답을 내고 있었고, 힌트가 그 답을 막고 있었다.


8. 힌트 유효기간 대장 ​

이 교재의 새 표준 문서다. 양식은 힌트_유효기간_대장.md에 있다.

왜 필요한가 ​

2018-11-06 회의록에는 현상, 조치, 결과, 결론이 다 있었다. 빠진 것은 한 줄이었다.

※ 측정 조건 : 운영 DB 복제본, 주문 20,000 건

이 줄은 첨부 자료 끝에 있긴 했지만 본문에는 없었고, 재검토 주기도 없었다. 그래서 데이터가 50배로 느는 동안 아무도 다시 재지 않았다.

힌트는 데이터를 보고 내린 판단이다.판단을 기록할 때 전제를 같이 적지 않으면, 전제가 바뀌어도 아무도 알아채지 못한다.

열 구성 (요약) ​

열무엇을 적나
힌트 / 위치USE_NL(c) · PKG_ORD_STLM.Q2
붙인 시점 / 근거2018-11-06 성능개선 회의록
그때의 데이터 규모 ★주문 20,000건 · M_CUS 2,000건
그때의 효과3초 → 0.4초(회의록 기재)
현재 규모주문 1,000,000건(50배)
현재 재측정값NL 1,026,849 vs 해시 5,869
판정폐기 / 유지 / 재검토
다음 재검토 조건 ★"M_CUS가 10배가 되면"처럼 숫자로

★ 표시한 두 칸 때문에 이 양식이 있다. 나머지는 보조 칸이다.

유지 판정도 있다 ​

대장은 "힌트를 다 떼라"는 문서가 아니다. 유지하기로 한 힌트에도 전제와 재검토 조건을 적어 두는 문서다. 오늘 만든 rev01(힌트 없음)도 M_CUS가 2,000행이라서 해시가 이긴 것이므로, 같은 대장에 한 줄을 차지한다.


9. 이 화의 측정값 전부 ​

모두 ora-hanorder에서 잰 값이다(2026-10-18). 환경: Oracle AI Database 26ai Free 23.26.2.0.0 · db_block_size 8192 · pga_aggregate_target 512M · workarea_size_policy AUTO · 히스토그램 없음(FOR ALL COLUMNS SIZE 1). 데이터: T_ORD_BIG 1,000,000 / M_CUS 2,000 / M_RGN 8 · 인덱스는 02화 산출물 2개와 PK.

Q2 리비전별 (실습/00) ​

단계SQL계획Buffersinner Startsplan_hash_value
[A] rev00힌트 그대로NESTED LOOPS1,026,8491,000,0002289018667
[C]rev00 + M_CUS 커버링 인덱스NESTED LOOPS + INDEX RANGE SCAN36,8521,000,0002696332737
[D] rev01힌트 제거HASH JOIN5,86912292855265
[E]LEADING(c o) USE_HASH(o) 명시HASH JOIN5,86812292855265
[F]LEADING(o c) USE_HASH(c) (build 뒤집기)HASH JOIN5,8681305033139
[G]USE_MERGEMERGE JOIN5,868—766613549
[H]rev00 − 스칼라 서브쿼리NESTED LOOPS1,026,8371,000,0003030409043

[A] → [D] = 1,026,849 → 5,869 = 175.0배. 결과 3행(900,000 / 90,000 / 10,000)은 모든 단계에서 같았다.

TABLE ACCESS FULL T_ORD_BIG 줄은 실행할 때마다 5,826 / 5,827로 1블록씩 흔들린다. 그래서 [A] 합계는 1,026,849 ~ 1,026,853, [D]는 5,868 ~ 5,869 사이를 오간다. 정상이다.

[A] 비용 분해 ​

자리StartsBuffers
TABLE ACCESS FULL T_ORD_BIG (outer)15,826
INDEX UNIQUE SCAN PK_M_CUS (inner 주소)1,000,00021,011
TABLE ACCESS BY INDEX ROWID M_CUS (inner 본문)1,000,0001,000,000
M_RGN (스칼라 서브쿼리)812
합계1,026,849

스칼라 서브쿼리 분리 (07화로 넘긴다) ​

Buffers
스칼라 포함 ([A])1,026,849
스칼라 제거 ([H])1,026,837
기여분12 (전체의 0.0012%)

M_RGN 쪽 Starts는 8이다. RGN_ID가 8종류뿐이라 오라클이 결과를 재사용했다. 그 메커니즘과, 언제 재사용이 안 되는지는 07화에서 다룬다. 여기서는 "범인이 아니다"까지만 확인한다.

크기 스윕 (실습/01) ​

outer 행수NL해시승자NL ÷ 해시
102536NL0.7
203536NL1.0
254036해시1.1
10011536해시3.2
2,0002,08845해시46.4
20,000 (2018년)20,798151해시137.7
200,000211,6651,229해시172.2
1,000,0001,026,8375,856해시175.3

손익분기점은 outer 21건이다(15 + N = 36). 해시 쪽은 NO_QUERY_TRANSFORMATION을 걸어 Group By Placement 변환을 막고 조인 방식만 비교했다.

⚠️ N ≥ 2,000 구간의 NL 값은 실행할 때마다 ±0.5% 흔들린다. 스크립트가 T_ORD_SZ를 매번 새로 쌓아서 블록 패킹이 조금씩 달라지기 때문이다(실측 폭: 2,000 → 2,081~2,095 · 20,000 → 20,798~20,833 · 200,000 → 211,102~212,411). 해시 쪽과 N ≤ 100 구간, 그리고 배수(약 138배, 172배, 175배)는 변하지 않는다.

work area (실습/02) ​

정책계획BuffersUsed-MemUsed-TmpReads/Writes시간
W1AUTOHASH JOIN5,869약 1,700K (0)—0 / 00.17초
W2MANUAL 64KBHASH JOIN(build 뒤집음)5,868약 250~300K (4)24M11,784 / 2,9525~8초
W3MANUAL 64KBMERGE JOIN6,573SORT JOIN 66,560 (1) / (100)1,024K / 25M13,930 / 13,93022~32초

W1의 Used-Mem은 실행할 때마다 1,679K ~ 1,739K로 흔들린다. 판정은 괄호 안의 (0)으로 한다.


10. 원천 좌표 ​

전체 지도 → 00_자료준비/06_원서와_공식문서_지도.md PDF 페이지 = 책 페이지 − 2 · 성능고도화II_목차색인.md (공개 사이트에는 없음)

성능고도화 II권 Ch2 — 조인 (주 원천) ​

주제절책 페이지
NL 조인 기본 메커니즘Ch2 01 (1)210
힌트로 NL 조인을 제어하는 방법Ch2 01 (2)211
NL 조인 수행 과정 분석Ch2 01 (3)214
NL 조인의 특징Ch2 01 (4)217
배치 I/O · 버퍼 pinning 효과Ch2 01 (7)(8)225 · 226
소트 머지 조인 기본 메커니즘Ch2 02 (1)235
소트 머지 조인의 특징Ch2 02 (2)238
조인 조건식이 등치(=)가 아닐 때Ch2 02 (5)246
해시 조인 기본 메커니즘Ch2 03 (1)249
두 가지 해시 조인 알고리즘 (Grace 분할 포함)Ch2 03 (3)252
메모리를 초과할 때 처리 방식Ch2 03 (4)256
해시 조인 사용 기준Ch2 03 (6)264
조인 순서의 중요성Ch2 04266
스칼라 서브쿼리를 이용한 조인 · 캐싱 효과Ch2 07 (1)(2)284 · 285 → 07화

오라클 공식문서 ​

주제매뉴얼장 제목
조인 방식 3종tgsqlJoins
힌트 문법과 적용 조건tgsqlInfluencing the Optimizer
PGA와 work areacncptMemory Architecture (Program Global Area)
work area 자동 관리tgdbaTuning the Database Memory (PGA Memory Management)

이 화에서 꼭 확인할 뷰 ​

sql
V$SQL_WORKAREA           -- policy · estimated_optimal_size · last_execution · last_tempseg_size
V$SQL_WORKAREA_ACTIVE    -- 지금 돌고 있는 work area (장시간 쿼리 관찰용)
V$SQL_PLAN_STATISTICS_ALL -- last_starts · last_cr_buffer_gets (DISPLAY_CURSOR 가 줄여 쓴 값의 원본)
V$PGASTAT                -- PGA 전체 사용량과 over-allocation 횟수

다음 화가 여기서 가져가는 것 ​

화가져가는 것
07M_RGN 쪽 Starts가 8인 이유: 스칼라 서브쿼리 캐싱은 왜 되고 언제 깨지나. 그리고 PGA에 래치가 없는 이유(= 버퍼 캐시에는 있는 이유)
08Q2가 100만 행을 읽어 3행을 만든다는 사실 자체. 그 요구가 맞는가 · 조인 카디널리티 · 쿼리 변환
09Used-Tmp와 패스 수. W2의 4 PASSES, W3의 100 PASSES를 그대로 들고 간다
10E-Rows 1000K vs A-Rows 1000K: 06화에서는 추정이 정확했다. 추정이 틀리면 조인 선택이 어떻게 무너지는가
18Q2 rev01의 재검토 조건: 대장에 적은 "M_CUS가 10배가 되면"을 실제로 다시 잰다