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

VIP 취소 주문 상황판은 늘 비어 있는데 1분마다 4만 블록을 읽는다

실전SELECT한빛유통622 / 700

상황 ​

고객관리팀은 VIP(A 등급) 고객의 취소를 이탈 징후로 본다. 상황판 다섯 대가 「VIP 최근 취소 20건」을 1분마다 새로 고친다. 지금까지 VIP의 취소는 한 건도 없어서 화면은 늘 비어 있는데, AWR에서 이 SQL이 Buffer Gets 2위다. 정하은이 물었다. "지난번 취소 정산 점검처럼 컬럼 그룹을 만들어서 두 열의 관계를 알려 주면 되지 않을까요?" 박팀장은 고칠 방법을 둘 이상 비교해서 고르라고 했다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_ST COMPLETE 900,000 · PENDING 80,000 · CANCEL 20,000(FREQUENCY 히스토그램, 정책서 1호) · 매주 일요일에 인덱스를 단 채 재적재한다
  • 취소는 고객 40명(모두 C 등급)에게서만 나온다. A와 B 등급 고객의 주문은 전부 COMPLETE다.
  • 인덱스: IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT): BLEVEL 2 · 리프 약 3,300 / IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT): BLEVEL 2 · 리프 약 3,400 · 행이 흩어져 있어 두 인덱스 모두 CF가 100만에 가깝다
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID) · CUS_GD A 20 · B 180 · C 1,800(FREQUENCY 히스토그램) · 고객당 주문 500건

원본 SQL ​

sql
SELECT *
  FROM (SELECT o.ORD_SEQ, o.ORD_DT, o.ORD_AMT, c.CUS_ID, c.CUS_NM
          FROM T_ORD_BIG o, M_CUS c
         WHERE o.ORD_ST = 'CANCEL'
           AND c.CUS_GD = 'A'
           AND c.CUS_ID = o.CUS_ID
         ORDER BY o.ORD_DT DESC)
 WHERE ROWNUM <= 20

실행계획 학습용 예시 · 고객과 상태의 관계는 교재 10화의 적재 데이터 그대로 ​

xplan
--------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |      1 |        |      0 |00:00:00.12 |   40489 |
|*  1 |  COUNT STOPKEY                   |                  |      1 |        |      0 |00:00:00.12 |   40489 |
|   2 |   VIEW                           |                  |      1 |     20 |      0 |00:00:00.12 |   40489 |
|   3 |    NESTED LOOPS                  |                  |      1 |     20 |      0 |00:00:00.12 |   40489 |
|   4 |     NESTED LOOPS                 |                  |      1 |   2000 |  20000 |00:00:00.07 |   20489 |
|   5 |      TABLE ACCESS BY INDEX ROWID | T_ORD_BIG        |      1 |  20000 |  20000 |00:00:00.05 |   20069 |
|*  6 |       INDEX RANGE SCAN DESCENDING| IX_ORD_BIG_ST_DT |      1 |   2000 |  20000 |00:00:00.01 |      69 |
|*  7 |      INDEX UNIQUE SCAN           | PK_M_CUS         |  20000 |      1 |  20000 |00:00:00.01 |     420 |
|*  8 |     TABLE ACCESS BY INDEX ROWID  | M_CUS            |  20000 |      1 |      0 |00:00:00.04 |   20000 |
--------------------------------------------------------------------------------------------------------
   1 - filter(ROWNUM<=20)
   6 - access("O"."ORD_ST"='CANCEL')
   7 - access("C"."CUS_ID"="O"."CUS_ID")
   8 - filter("C"."CUS_GD"='A')

문제 ​

  1. 옵티마이저가 취소 인덱스를 거꾸로 읽는 계획을 고른 이유를 조인 결과의 E-Rows를 손으로 계산해 설명하고, 그 계획이 실제로 취소 2만 건을 끝까지 읽은 이유를 쓰시오.
  2. 정하은의 제안이 이 SQL에 통하지 않는 이유를 쓰고, 고칠 방법을 둘 이상 비교해 하나를 골라 SQL이나 DDL로 쓰시오.
  3. 고른 방법의 대가와, 언젠가 VIP 취소가 생겼을 때 이 계획이 어떻게 되는지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자