Skip to content
튜닝 연습소트 튜닝과 페이징
SORT-059

추출 조건에 「전부」를 더한 뒤로 고객별 최근 주문 3건 추출이 TEMP를 37MB 쓴다

중급SELECT한빛유통364 / 700

상황 ​

CRM팀은 매주 월요일 06시에 추천 엔진으로 넘길 「고객별 최근 주문 n건」을 추출한다. 평소에는 3건이다. 지난달 데이터팀이 "분기마다 한두 번은 고객의 주문을 전부 순번과 함께 받고 싶다"고 해서, 개발자가 n에 0을 넣으면 전부 나오도록 조건을 고쳤다. 그 뒤로 평소의 3건 추출이 0.4초에서 2.6초로 늘었고, 돌 때마다 TEMP를 37MB 쓴다. 같은 시각에 정산 배치가 돌아서 DBA는 TEMP 경보를 받는다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 고객 2,000명(고객마다 약 500건) · ORD_DT DATE(전부 자정) · ORD_SEQ 유일 · 한 고객의 주문은 테이블 곳곳의 블록에 흩어져 있다
  • 인덱스 IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT): CS 화면용 · BLEVEL 2 · 리프 약 3,000블록 · 통계 최신
  • 월요일 06시에는 정산 배치와 겹쳐 work area 하나가 쓸 수 있는 메모리가 약 20MB로 내려간다
  • :n = 3 추출은 매주, :n = 0(전부) 추출은 분기에 한두 번 돈다

원본 SQL ​

sql
SELECT CUS_ID, ORD_SEQ, ORD_DT, ORD_AMT, rn
  FROM (SELECT o.CUS_ID, o.ORD_SEQ, o.ORD_DT, o.ORD_AMT,
               ROW_NUMBER() OVER (PARTITION BY o.CUS_ID
                                  ORDER BY o.ORD_DT DESC, o.ORD_SEQ DESC) AS rn
          FROM T_ORD_BIG o)
 WHERE (:n = 0 OR rn <= :n)

실행계획 학습용 예시 · 메모리 크기는 교재 09화 규모 ​

xplan
[지금 · :n = 3 · 월요일 06:00]
-----------------------------------------------------------------------------------------------------------------------
| Id  | Operation            | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem | Used-Mem | Used-Tmp|
-----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |      1 |        |   6000 |00:00:02.63 |    5826 |       |          |         |
|*  1 |  VIEW                |           |      1 |  50000 |   6000 |00:00:02.63 |    5826 |       |          |         |
|   2 |   WINDOW SORT        |           |      1 |   1000K|   1000K|00:00:02.41 |    5826 |    44M|   20M (1)|      37M|
|   3 |    TABLE ACCESS FULL | T_ORD_BIG |      1 |   1000K|   1000K|00:00:00.06 |    5826 |       |          |         |
-----------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter((:N=0 OR "RN"<=:N))

[지난달까지 · WHERE rn <= :n · :n = 3 · 같은 시각]
   2번 줄이 WINDOW SORT PUSHED RANK · A-Rows 6000 · Used-Mem 410K (0) · Used-Tmp 없음 · A-Time 0.38초 · Buffers 5,826
   2 - filter(ROW_NUMBER() OVER ( PARTITION BY "O"."CUS_ID" ORDER BY INTERNAL_FUNCTION("O"."ORD_DT") DESC ,
              INTERNAL_FUNCTION("O"."ORD_SEQ") DESC )<=:N)

문제 ​

  1. 지난달까지와 지금의 실행계획 2번 줄을 비교해, 3건만 받는데도 100만 행을 다 정렬하고 TEMP를 쓰는 까닭을 설명하시오.
  2. 매주 도는 3건 추출이 다시 고객마다 3건만 남기며 정렬하도록 SQL을 고치고, '전부' 요구는 어떻게 처리할지 쓰시오.
  3. 고친 뒤에도 Buffers가 5,826인 이유와, 이 추출에 IX_ORD_BIG_CUS_DT를 쓰지 않는 편이 나은 이유를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자