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

인덱스 정리를 시작한 날 오후, 계획을 고정해 두었던 매장 일 판매 화면이 판매 테이블을 통째로 읽는다

중급DDL유통636 / 700

상황 ​

오과장이 인덱스 정리를 시작했다. 04화의 절차대로 지울 후보를 먼저 INVISIBLE로 바꿔 두고 일주일 지켜볼 생각이었다. 14:10에 첫 후보 IX_CF10_DT_SHP를 INVISIBLE로 바꿨다. "SLE_NO가 날짜로 시작하니까 PK로도 충분할 거예요." 14:30부터 점장 화면 「오늘 매장 판매」(하루 약 600번)가 느려졌다. 이 화면 SQL에는 작년 9월에 베이스라인을 올려 두었다. 조민우가 물었다. "베이스라인이 있는데 계획이 왜 바뀌어요?"

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) · 하루 약 700건 · 매장 6곳
  • CF10의 인덱스: CF10_PK (SLE_NO) · IX_CF10_DT_SHP (SLE_DT, SHP_NO)(오늘 14:10 INVISIBLE) · 다른 인덱스 없음
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO)
  • 베이스라인: 이 SQL에 받아들인 계획 하나(MANUAL-LOAD-FROM-CURSOR-CACHE · 작년 9월 · IX_CF10_DT_SHP → CF11_PK의 NL · 389블록)
  • OPTIMIZER_USE_INVISIBLE_INDEXES = FALSE(기본값)

원본 SQL ​

sql
-- 오과장 14:10
ALTER INDEX IX_CF10_DT_SHP INVISIBLE;

-- 점장 화면 「오늘 매장 판매」
SELECT a.SLE_NO, a.SLE_HMS, b.ITM_NO, b.SLE_QTY, b.SLE_UPRC
  FROM CF10 a, CF11 b
 WHERE a.SLE_DT = :dt
   AND a.SHP_NO = :shp
   AND b.SLE_NO = a.SLE_NO

실행계획 학습용 예시 · OPT-005와 같은 규모 ​

xplan
[14:30 이후]
---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |    256 |00:00:00.12 |    4961 |
|   1 |  NESTED LOOPS                |         |      1 |    250 |    256 |00:00:00.12 |    4961 |
|   2 |   NESTED LOOPS               |         |      1 |    250 |    256 |00:00:00.12 |    4821 |
|*  3 |    TABLE ACCESS FULL         | CF10    |      1 |    114 |    117 |00:00:00.11 |    4583 |
|*  4 |    INDEX RANGE SCAN          | CF11_PK |    117 |      2 |    256 |00:00:00.01 |     238 |
|   5 |   TABLE ACCESS BY INDEX ROWID| CF11    |    256 |      2 |    256 |00:00:00.01 |     140 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter(("A"."SLE_DT"=:DT AND "A"."SHP_NO"=:SHP))
   4 - access("B"."SLE_NO"="A"."SLE_NO")

(Note 절 없음 · 14:10 이전에는 "SQL plan baseline SQL_PLAN_8u2vbm4c0q7xa5d91e3f0 used" · 389블록)

[DBA_SQL_PLAN_BASELINES — 이 SQL · 15:00]
 PLAN_NAME                        ORIGIN                          ENABLED  ACCEPTED  REPRODUCED  LAST_EXECUTED
 SQL_PLAN_8u2vbm4c0q7xa5d91e3f0   MANUAL-LOAD-FROM-CURSOR-CACHE   YES      YES       NO          14:09
 SQL_PLAN_8u2vbm4c0q7xa2b4c6e81   AUTO-CAPTURE                    YES      NO        YES         -

[받아들인 계획의 Outline — DISPLAY_SQL_PLAN_BASELINE 에서 필요한 줄만]
   INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("CF10"."SLE_DT" "CF10"."SHP_NO"))
   LEADING(@"SEL$1" "A"@"SEL$1" "B"@"SEL$1")
   USE_NL(@"SEL$1" "B"@"SEL$1")

문제 ​

  1. 베이스라인이 있는데도 계획이 바뀐 이유를 베이스라인이 계획을 저장하는 방식과 REPRODUCED 칸으로 설명하시오.
  2. 지금 할 응급 조치를 SQL로 쓰고, 인덱스를 감추거나 지우기 전에 확인할 것을 정리하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자