Skip to content
튜닝 연습인덱스 사용 조건
IDX-060

본사 주문 조회 화면이 매장을 골라도 일주일 치를 읽고, 메뉴로 찾으면 3만 블록 가까이 읽는다

실전DDL버거60 / 700

상황 ​

버거 본사 「주문 조회」 화면은 기간(필수, 최대 7일)에 매장, 메뉴, 시간대를 옵션으로 더해 주문을 찾는다. SQL은 한 문장이고 옵션마다 (:v IS NULL OR ...)를 달았다. 메뉴 조건은 주문상품에 그 메뉴가 있는지 EXISTS로 확인한다. 강도현이 한 달 치 호출 로그를 세어 보니 네 조합이 거의 전부였다. C1 기간 + 매장(하루 약 1,200번), C2 기간 + 매장 + 시간대(약 500번), C3 기간 + 메뉴(약 200번), C4 기간 + 시간대(약 100번)다. 박팀장은 "조합마다 SQL을 어떻게 나누고 인덱스를 몇 개 둘지, POS 주문 적재가 얼마나 무거워지는지까지 적어 와라"고 했다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_NO(PK) · ORD_DT VARCHAR2(8) · ORD_HMS VARCHAR2(6) · SHOP_NO(매장 200곳) · 주문 일자 순으로 쌓였다 · 하루 평균 1,368건, 7일 약 9,576건, 매장 하나의 7일 약 48건
  • ORD_ITEM 주문상품: 2,472,717행 · 55,265블록 · ORD_NO · ITEM_ID(메뉴 92종) · ORD_ITEM_QTY · ORD_DT · ORD_HMS(주문의 값을 그대로 복사해 둔 컬럼) · 주문 하나에 평균 2.5줄 · 메뉴 하나의 7일 약 260줄
  • 인덱스 ORD_PK (ORD_NO) · ORD_X01 (ORD_DT, ORD_HMS) · ORD_ITEM_PK (ORD_NO, ITEM_ID) · ORD_ITEM_X01 (ORD_DT, ORD_HMS) · 모두 BLEVEL 2 · 통계 최신
  • POS는 하루 약 1,400건의 주문과 3,400줄의 주문상품을 넣는다

원본 SQL ​

sql
SELECT o.ORD_NO, o.ORD_DT, o.ORD_HMS, o.SHOP_NO
  FROM ORD o
 WHERE o.ORD_DT BETWEEN :v_from AND :v_to
   AND (:v_shop IS NULL OR o.SHOP_NO = :v_shop)
   AND (:v_hms_from IS NULL OR o.ORD_HMS BETWEEN :v_hms_from AND :v_hms_to)
   AND (:v_item IS NULL OR EXISTS (SELECT 1
                                     FROM ORD_ITEM i
                                    WHERE i.ORD_NO  = o.ORD_NO
                                      AND i.ITEM_ID = :v_item))
 ORDER BY o.ORD_DT, o.ORD_HMS

실행계획 학습용 예시 ​

xplan
[C1 · 매장 SH0042 · 2013-03-11 ~ 2013-03-17]
--------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Starts | E-Rows | A-Rows | A-Time      | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |      1 |        |     47 | 00:00:00.03 |     251 |
|*  1 |  FILTER                       |             |      1 |        |     47 | 00:00:00.03 |     251 |
|*  2 |   TABLE ACCESS BY INDEX ROWID | ORD         |      1 |     48 |     47 | 00:00:00.03 |     251 |
|*  3 |    INDEX RANGE SCAN           | ORD_X01     |      1 |   9576 |   9576 | 00:00:00.01 |      36 |
|*  4 |   INDEX UNIQUE SCAN           | ORD_ITEM_PK |      0 |      1 |      0 | 00:00:00.01 |       0 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter((:V_ITEM IS NULL OR  IS NOT NULL))
   2 - filter((:V_SHOP IS NULL OR "O"."SHOP_NO"=:V_SHOP))
   3 - access("O"."ORD_DT">=:V_FROM AND "O"."ORD_DT"<=:V_TO)
       filter((:V_HMS_FROM IS NULL OR ("O"."ORD_HMS">=:V_HMS_FROM AND "O"."ORD_HMS"<=:V_HMS_TO)))
   4 - access("I"."ORD_NO"=:B1 AND "I"."ITEM_ID"=:V_ITEM)

[C3 · 메뉴 23 · 같은 7일 · 같은 커서]
--------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Starts | E-Rows | A-Rows | A-Time      | Buffers |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |      1 |        |    261 | 00:00:00.21 |   28979 |
|*  1 |  FILTER                       |             |      1 |        |    261 | 00:00:00.21 |   28979 |
|*  2 |   TABLE ACCESS BY INDEX ROWID | ORD         |      1 |     48 |   9576 | 00:00:00.03 |     251 |
|*  3 |    INDEX RANGE SCAN           | ORD_X01     |      1 |   9576 |   9576 | 00:00:00.01 |      36 |
|*  4 |   INDEX UNIQUE SCAN           | ORD_ITEM_PK |   9576 |      1 |    261 | 00:00:00.17 |   28728 |
--------------------------------------------------------------------------------------------------------

문제 ​

  1. 지금 SQL로 C1과 C3를 실행할 때 각각 무엇을 읽는지 설명하고, C3가 3만 블록 가까이 읽는 이유를 Starts로 설명하시오.
  2. 조합에 맞게 SQL을 나누고, 꼭 필요한 인덱스만 DDL로 작성하시오. 네 조합이 각각 어느 인덱스를 쓰는지도 밝히시오.
  3. 새 인덱스가 POS 주문 적재에 주는 부담을 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자