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

주문 인덱스를 하나 줄이자는 안에 점주 앱이 내림차순 인덱스를 쓰고 있어 못 지운다는 반대가 나왔다

중급DDL버거70 / 700

상황 ​

버거원 DBA 오과장이 분기 인덱스 점검에서 주문 테이블의 ORD_X05를 정리하자고 올렸다. 작년에 개발자가 본사 「매장별 오늘 주문」(S3)의 정렬을 없애겠다며 만든 내림차순 인덱스다. 장애 뒤 재적재 때마다 인덱스 하나만큼 시간과 redo가 더 든다는 것이 이유다. 조민우는 반대했다. "V$SQL_PLAN을 보니 점주 앱 「최근 주문 20건」(S1)이 하루 4만 번 X05를 씁니다. 지우면 점주 앱이 매번 정렬을 하게 됩니다." 박팀장은 둘 중 누구 말이 맞는지 숫자로 판정해 오라고 했다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · 주문 일시 순으로 쌓였다(하루치가 약 31블록 안에 모여 있다) · ORD_NO · ORD_DT VARCHAR2(8) · ORD_HMS VARCHAR2(6) · SHOP_NO VARCHAR2(10) 200곳 · 하루 약 1,370건, 매장 하나의 하루 주문은 평균 7건
  • 인덱스(모두 BLEVEL 2 · 통계 최신)
    • ORD_PK (ORD_NO)
    • ORD_X01 (ORD_DT, ORD_HMS): 리프 3,774블록
    • ORD_X02 (SHOP_NO, ORD_DT, ORD_HMS): 점주 앱용 · 리프 약 4,050블록
    • ORD_X05 (SHOP_NO, ORD_DT DESC, ORD_HMS DESC): 작년에 S3용으로 만든 내림차순 인덱스 · 리프 약 4,050블록 · CF도 ORD_X02와 같다
  • POS가 주문마다 INSERT한다(하루 약 1,370건) · 장애 뒤 재적재 때는 100만 건을 다시 넣는다(인덱스 하나에 5만 건당 redo 약 12MB, 교재 14화)

원본 SQL ​

sql
-- S1 점주 앱 「최근 주문 20건」 (하루 약 4만 번)
SELECT ORD_NO, ORD_DT, ORD_HMS
  FROM ORD
 WHERE SHOP_NO = :v_shop
 ORDER BY ORD_DT DESC, ORD_HMS DESC
 FETCH FIRST 20 ROWS ONLY;

-- S2 점주 앱 「기간 주문 목록」 (하루 수천 번)
SELECT ORD_NO, ORD_DT, ORD_HMS
  FROM ORD
 WHERE SHOP_NO = :v_shop
   AND ORD_DT BETWEEN :v_from AND :v_to
 ORDER BY ORD_DT, ORD_HMS;

-- S3 본사 「매장별 오늘 주문」 (본사 PC 10대가 5분마다)
SELECT SHOP_NO, ORD_HMS, ORD_NO
  FROM ORD
 WHERE ORD_DT = :v_dt
 ORDER BY SHOP_NO, ORD_HMS DESC;

-- 작년에 S3 의 정렬을 없애려고 만든 인덱스
CREATE INDEX ORD_X05 ON ORD (SHOP_NO, ORD_DT DESC, ORD_HMS DESC);

실행계획 학습용 예시 ​

xplan
[S1 · 지금 · SH0042]
----------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |         |      1 |        |     20 |00:00:00.01 |      24 |
|*  1 |  VIEW                          |         |      1 |     20 |     20 |00:00:00.01 |      24 |
|*  2 |   WINDOW NOSORT STOPKEY        |         |      1 |     20 |     20 |00:00:00.01 |      24 |
|   3 |    TABLE ACCESS BY INDEX ROWID | ORD     |      1 |     20 |     20 |00:00:00.01 |      24 |
|*  4 |     INDEX RANGE SCAN           | ORD_X05 |      1 |     20 |     20 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------------------
   1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=20)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("ORD_DT") DESC ,INTERNAL_FUNCTION("ORD_HMS") DESC )<=20)
   4 - access("SHOP_NO"=:V_SHOP)

[S3 · 지금 · 2013-03-15]
----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |         |      1 |        |   1370 |00:00:00.01 |      38 |          |
|   1 |  SORT ORDER BY                        |         |      1 |   1370 |   1370 |00:00:00.01 |      38 |      83K |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED | ORD     |      1 |   1370 |   1370 |00:00:00.01 |      38 |          |
|*  3 |    INDEX RANGE SCAN                   | ORD_X01 |      1 |   1370 |   1370 |00:00:00.01 |       7 |          |
----------------------------------------------------------------------------------------------------------------------
   3 - access("ORD_DT"=:V_DT)

[S3 · 같은 날 · INDEX_SS(ORD ORD_X05) 힌트로 잰 것]
--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |   1370 |00:00:00.03 |    1968 |
|   1 |  TABLE ACCESS BY INDEX ROWID | ORD     |      1 |   1370 |   1370 |00:00:00.03 |    1968 |
|*  2 |   INDEX SKIP SCAN            | ORD_X05 |      1 |   1370 |   1370 |00:00:00.01 |     598 |
--------------------------------------------------------------------------------------------------
   2 - access(SYS_OP_DESCEND("ORD_DT")=SYS_OP_DESCEND(:V_DT))
       filter(SYS_OP_UNDESCEND(SYS_OP_DESCEND("ORD_DT"))=:V_DT)

[V$SQL_PLAN · 지난 2주]  ORD_X05를 쓰는 SQL은 S1 하나다 · S2는 ORD_X02를 쓰고, S3는 늘 ORD_X01과 SORT ORDER BY다

문제 ​

  1. 조민우의 반대(ORD_X05를 지우면 S1이 정렬을 한다)가 맞는지 S1의 조건과 ORD_X02의 컬럼 순서로 판정하시오.
  2. ORD_X05를 만든 본래 목적(S3의 정렬 없애기)이 맞는 판단이었는지 두 계획의 숫자로 설명하시오.
  3. ORD_X05를 정리하는 DDL을 작성하고, 지우기 전에 확인할 것과 지워서 얻는 것을 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자