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

인덱스를 더 잘 타게 하는 설정이 들어간 다음 날부터 월 상태별 매출 집계가 블록을 다섯 배 읽는다

기초SELECT한빛유통624 / 700

상황 ​

지난주 화요일, 고객 화면에서 "인덱스가 있는데 안 탄다"는 민원이 들어오자 오과장은 운영에 설정 한 줄을 넣었다. 민원 화면은 빨라졌다고 한다. 수요일부터 정산팀의 「월 상태별 매출」 리포트(하루 약 300번)가 읽는 블록이 다섯 배 가까이 늘었다. 리포트 SQL과 인덱스, 통계는 지난주와 같다. 아래는 6월 한 달을 넘겼을 때의 계획이다. 비교하려고 같은 SQL에 FULL 힌트를 넣어 한 번 더 떴다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_DT NDV 1,095(2022-01-01 ~ 2024-12-30) · 6월 27,390행 · ORD_ST 3종
  • 인덱스 IX_ORD_BIG_DT (ORD_DT): BLEVEL 2 · 리프 2,653블록 · 클러스터링 팩터 1,000,000
  • 시스템 통계는 모은 적이 없다(기본값). 이 테이블을 FULL로 읽는 비용은 1,612(IO 1,606)다.
  • 경고 로그(alert log): ALTER SYSTEM SET optimizer_index_cost_adj=5 SCOPE=BOTH; (지난주 화요일 17:42)

원본 SQL ​

sql
SELECT ORD_ST, COUNT(*) AS cnt, SUM(ORD_AMT) AS amt
  FROM T_ORD_BIG
 WHERE ORD_DT >= :v_from
   AND ORD_DT <  :v_to
 GROUP BY ORD_ST

실행계획 학습용 예시 · 교재 04화 · 10화의 비용 숫자 ​

xplan
[지금 — 6월 한 달]
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |      1 |        |  1426 (100)|      3 |00:00:00.21 |   27466 |
|   1 |  HASH GROUP BY                       |               |      1 |      3 |  1426   (1)|      3 |00:00:00.21 |   27466 |
|*  2 |   FILTER                             |               |      1 |        |            |  27390 |00:00:00.19 |   27466 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG    |      1 |  28336 |  1421   (0)|  27390 |00:00:00.18 |   27466 |
|*  4 |     INDEX RANGE SCAN                 | IX_ORD_BIG_DT |      1 |  28336 |     4   (0)|  27390 |00:00:00.01 |      76 |
------------------------------------------------------------------------------------------------------------------------

[같은 SQL + /*+ FULL(T_ORD_BIG) */ — 비교용]
------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name          | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |               |      1 |        |  1616 (100)|      3 |00:00:00.09 |    5826 |
|   1 |  HASH GROUP BY                       |               |      1 |      3 |  1616   (1)|      3 |00:00:00.09 |    5826 |
|*  2 |   FILTER                             |               |      1 |        |            |  27390 |00:00:00.08 |    5826 |
|*  3 |    TABLE ACCESS FULL                 | T_ORD_BIG     |      1 |  28336 |  1612   (1)|  27390 |00:00:00.08 |    5826 |
------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(:V_FROM<:V_TO)
   4 - access("ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO)       (위 계획)
   3 - filter(("ORD_DT">=:V_FROM AND "ORD_DT"<:V_TO))     (아래 계획)

[V$PARAMETER]
 NAME                       VALUE   ISMODIFIED
 optimizer_index_cost_adj   5       SYSTEM_MOD

문제 ​

  1. 인덱스 경로의 Cost 1,421이 어떻게 나왔는지 기본값(100)일 때의 비용과 함께 손으로 계산하고, 옵티마이저가 FULL(1,612)을 버린 이유를 쓰시오.
  2. 설정을 바로잡는 SQL을 쓰고, 되돌리기 전에 확인할 것과 민원 화면은 어떻게 할지 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자