Skip to content
튜닝 연습옵티마이저와 통계
OPT-036

월 상품 판매 집계 배치 안에서만 같은 SELECT가 블록 17만 개를 읽는다

중급PLSQL버거626 / 700

상황 ​

매달 1일 새벽 PKG_MON.P_ITEM_SUM이 지난달 상품별 판매 수량을 MON_ITEM_SUM에 싣는다. 조민우가 이 INSERT의 SELECT 부분만 떼어 SQL Developer에서 돌리면 56,336블록인데, 배치 로그의 같은 문장은 168K 블록을 읽었다. 조민우가 배치와 같은 세션 조건에서 10053 트레이스를 떴다. 패키지 맨 앞 두 줄은 2015년에 들어갔고, 커밋 메시지에는 "인덱스 우선 튜닝 가이드 적용"이라고만 적혀 있다.

테이블과 인덱스 ​

  • ORD: 1,000,000행 · 22,422블록 · ORD_X01 (ORD_DT, ORD_HMS) 리프 3,774 · 클러스터링 팩터 22,223(날짜순 적재) · 한 달 약 41,700건
  • ORD_DT에는 HYBRID 히스토그램이 있어 한 달 범위의 E-Rows가 실제와 비슷하다.
  • ORD_ITEM: 2,472,717행 · 55,265블록 · ORD_ITEM_PK (ORD_NO, ITEM_ID) 높이 3 · 주문당 2.47줄(한 주문의 줄은 같은 블록에 있다)
  • MON_ITEM_SUM (YM, ITEM_ID, QTY): 이 배치가 쓰는 월 집계 테이블 · 달마다 92행 · 인덱스 없음
  • 인스턴스 파라미터는 기본값(optimizer_index_cost_adj 100, optimizer_index_caching 0) · 통계 최신

원본 SQL ​

sql
PROCEDURE P_ITEM_SUM(p_ym VARCHAR2) IS
BEGIN
  EXECUTE IMMEDIATE 'ALTER SESSION SET OPTIMIZER_INDEX_COST_ADJ = 1';    -- 2015 인덱스 우선 튜닝 가이드
  EXECUTE IMMEDIATE 'ALTER SESSION SET OPTIMIZER_INDEX_CACHING = 100';
  DELETE FROM MON_ITEM_SUM WHERE YM = p_ym;
  INSERT INTO MON_ITEM_SUM (YM, ITEM_ID, QTY)
  SELECT p_ym, i.ITEM_ID, SUM(i.ORD_ITEM_QTY)
    FROM ORD o, ORD_ITEM i
   WHERE o.ORD_DT BETWEEN p_ym || '01' AND p_ym || '31'
     AND i.ORD_NO = o.ORD_NO
   GROUP BY i.ITEM_ID;
  COMMIT;
END;

실행계획 학습용 예시 · 10053은 필요한 줄만 옮겼다 ​

xplan
[배치 안의 INSERT — p_ym = '201311']
-----------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name         | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT                        |              |      1 |        |      0 |00:00:01.38 |     168K|
|   1 |  LOAD TABLE CONVENTIONAL                | MON_ITEM_SUM |      1 |        |      0 |00:00:01.38 |     168K|
|   2 |   HASH GROUP BY                         |              |      1 |     92 |     92 |00:00:01.37 |     168K|
|*  3 |    FILTER                               |              |      1 |        |    103K|00:00:01.34 |     168K|
|   4 |     NESTED LOOPS                        |              |      1 |    103K|    103K|00:00:01.33 |     168K|
|   5 |      NESTED LOOPS                       |              |      1 |    103K|    103K|00:00:00.71 |     126K|
|   6 |       TABLE ACCESS BY INDEX ROWID BATCHED| ORD         |      1 |  41700 |  41712 |00:00:00.03 |    1071 |
|*  7 |        INDEX RANGE SCAN                 | ORD_X01      |      1 |  41700 |  41712 |00:00:00.01 |     139 |
|*  8 |       INDEX RANGE SCAN                  | ORD_ITEM_PK  |  41712 |      2 |    103K|00:00:00.40 |     125K|
|   9 |      TABLE ACCESS BY INDEX ROWID        | ORD_ITEM     |    103K|      1 |    103K|00:00:00.36 |   41712 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter(:B1||'01'<=:B1||'31')
   7 - access("O"."ORD_DT">=:B1||'01' AND "O"."ORD_DT"<=:B1||'31')
   8 - access("I"."ORD_NO"="O"."ORD_NO")

[10053 — 배치와 같은 세션 조건 · 필요한 줄만]
PARAMETERS WITH ALTERED VALUES
******************************
Compilation Environment Dump
optimizer_index_cost_adj            = 1
optimizer_index_caching             = 100
...
NL Join
  Outer table: Card: 41700.00  Cost: 11.21  Resp: 11.21  Degree: 1
  Inner table: ORD_ITEM  Alias: I
  Access Path: index (RangeScan)
    Index: ORD_ITEM_PK
    resc_io: 0.03  resc_cpu: 21934
  NL Join : Cost: 1262.48  Resp: 1262.48  Degree: 1
HA Join
  HA cost: 15409.66
Best:: JoinMethod: NestedLoop
       Cost: 1262.48  Degree: 1  Resp: 1262.48  Card: 102999.00

[같은 SELECT — SQL Developer 세션(파라미터 기본값)]  HASH JOIN · ORD_ITEM FULL · Buffers 56,336 · 0.61초
  (10053 : NL Join Cost 167,9xx · HA cost 16,4xx → HASH)
(126K = 126,207 · 125K = 125,136 · 168K = 167,919)

문제 ​

  1. 10053의 어느 부분에서 배치 세션만 다른 계획을 세운 이유를 찾을 수 있는지 쓰고, 두 파라미터가 NL과 해시 조인의 비용을 각각 어떻게 바꿨는지 설명하시오.
  2. 배치를 고치고 그 효과를 숫자로 쓰시오. 이런 세션 설정을 남겨 두면 안 되는 이유도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자