Skip to content
튜닝 연습파티션
PART-020

고객 상담 화면의 6개월 주문 조회가 칸 여섯 개만 여는데도 600블록이 넘는다

중급SELECT한빛유통395 / 700

상황 ​

한빛유통 고객센터의 「최근 주문」 탭은 고객 한 명의 최근 6개월 주문을 최신순으로 보여 준다. 상담원이 전화를 받을 때마다 열어서 하루 약 3,000번 호출된다. T_ORD_BIG을 월별 파티션으로 옮기면서 DBA 표준 "로컬 인덱스는 파티션 키를 맨 앞에 둔다(Prefixed)"대로 인덱스를 만들었다. 박팀장: "칸도 여섯 개만 열고 인덱스도 타는데, 왜 한 번에 600블록이 넘나."

테이블과 인덱스 ​

  • T_ORD_BIG_P14 주문(파티션 복사본, 교재 14화): 1,000,000행 · PARTITION BY RANGE (ORD_DT) 월별 36칸(P202201 ~ P202412)과 빈 PMAX, 모두 37칸. P202401은 25번째, P202406은 30번째 칸이다. 칸 하나는 약 27,400 ~ 27,800행이다.
  • ORD_DT는 DATE이고 전부 자정이다. 고객은 2,000명이고 고객마다 약 500건(한 달에 약 14건)이다. 고객 CUS_000123의 2024년 상반기 주문은 83건이다.
  • LOCAL 인덱스 IX_P14_DT_CUS (ORD_DT, CUS_ID): 칸마다 리프 약 95블록, 높이 2. 다른 인덱스는 없다. 통계는 최신이다.
  • 매월 1일 가장 오래된 칸을 DROP한다(보관 36개월).

원본 SQL ​

sql
SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
  FROM T_ORD_BIG_P14
 WHERE CUS_ID = :v_cus
   AND ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
   AND ORD_DT <  TO_DATE(:v_to, 'YYYYMMDD')
 ORDER BY ORD_DT DESC

실행계획 학습용 예시 · 교재 14화 규모 ​

xplan
[:v_cus = 'CUS_000123', :v_from = '20240101', :v_to = '20240701']
-------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                     | Name          | Starts | E-Rows | A-Rows |      A-Time | Buffers | Pstart | Pstop |
-------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                              |               |      1 |        |     83 | 00:00:00.01 |     661 |        |       |
|   1 |  SORT ORDER BY                                |               |      1 |     83 |     83 | 00:00:00.01 |     661 |        |       |
|*  2 |   FILTER                                      |               |      1 |        |     83 | 00:00:00.01 |     661 |        |       |
|   3 |    PARTITION RANGE ITERATOR                   |               |      1 |     83 |     83 | 00:00:00.01 |     661 |    KEY |   KEY |
|   4 |     TABLE ACCESS BY LOCAL INDEX ROWID BATCHED | T_ORD_BIG_P14 |      6 |     83 |     83 | 00:00:00.01 |     661 |    KEY |   KEY |
|*  5 |      INDEX RANGE SCAN                         | IX_P14_DT_CUS |      6 |     83 |     83 | 00:00:00.01 |     578 |    KEY |   KEY |
-------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(TO_DATE(:V_TO,'YYYYMMDD')>TO_DATE(:V_FROM,'YYYYMMDD'))
   5 - access("ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "CUS_ID"=:V_CUS AND "ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD'))
       filter("CUS_ID"=:V_CUS)

  (실행 때 고른 칸: 25 ~ 30번, P202401 ~ P202406)

문제 ​

  1. 칸을 여섯 개만 열고 인덱스도 쓰는데 661블록을 읽는 까닭을 실행계획의 Starts, Buffers, Predicate Information으로 설명하시오.
  2. SQL은 그대로 두고 인덱스를 다시 설계하시오. "로컬 인덱스는 Prefixed로 만든다"는 표준이 이 화면에서 왜 맞지 않는지 쓰시오.
  3. 조회 기간을 3년으로 넓혀 달라는 요청이 오면 어떤 인덱스가 나은지, 그 대가와 함께 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자