Skip to content
튜닝 연습파싱과 바인드 변수
PARSE-011

개발 DB에서는 함수 기반 인덱스를 타던 월별 화면이 운영에서는 FULL로 돈다

중급SELECT유통556 / 700

상황 ​

작년 가을 공유 풀 경보가 울렸을 때 DBA가 유통 운영 DB 인스턴스에 CURSOR_SHARING = FORCE를 켰고, 지금도 그대로다. 개발 DB는 기본값(EXACT)이다. 이번 달 조민우가 「매장 월별 요일 분포」 화면을 만들면서 함수 기반 인덱스를 하나 만들었다. 개발 DB에서는 0.01초였는데, 운영에 올리자 화면을 열 때마다 4,583블록을 읽는다.

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · SLE_DT VARCHAR2(8) '20230601' · SHP_NO NUMBER(매장 6곳) · SLE_DOW 요일
  • 매장 한 곳의 한 달 판매는 약 3,470건이다.
  • 인덱스 CF10_PK (SLE_NO) · 이 화면 때문에 만든 함수 기반 인덱스 IX_CF10_SHP_YM (SHP_NO, SUBSTR(SLE_DT, 1, 6)) · 통계 최신
  • 운영 CURSOR_SHARING = FORCE(인스턴스 전체) · 개발 EXACT

원본 SQL ​

sql
-- 조민우 작성 : 조회월(:ym, 'YYYYMM')과 매장(:shp)은 이미 바인드 변수다
SELECT SLE_DOW, COUNT(*) AS SLE_CNT
  FROM CF10
 WHERE SHP_NO = :shp
   AND SUBSTR(SLE_DT, 1, 6) = :ym
 GROUP BY SLE_DOW
 ORDER BY SLE_DOW;

CREATE INDEX IX_CF10_SHP_YM ON CF10 (SHP_NO, SUBSTR(SLE_DT, 1, 6));

실행계획 학습용 예시 ​

xplan
[운영 V$SQL · SQL_TEXT]
  SELECT SLE_DOW, COUNT(*) AS SLE_CNT FROM CF10 WHERE SHP_NO = :shp AND SUBSTR(SLE_DT, :"SYS_B_0", :"SYS_B_1") = :ym GROUP BY SLE_DOW ORDER BY SLE_DOW

[운영 · FORCE]
Plan hash value: 2771390418

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      7 |00:00:00.09 |    4583 |
|   1 |  SORT GROUP BY     |      |      1 |      7 |      7 |00:00:00.09 |    4583 |
|*  2 |   TABLE ACCESS FULL| CF10 |      1 |    833 |   3468 |00:00:00.08 |    4583 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter(("SHP_NO"=:SHP AND SUBSTR("SLE_DT",:SYS_B_0,:SYS_B_1)=:YM))

[개발 · EXACT · 같은 SQL]
Plan hash value: 1046282913

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |      7 |00:00:00.01 |     207 |
|   1 |  SORT GROUP BY                       |                |      1 |      7 |      7 |00:00:00.01 |     207 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| CF10           |      1 |   3472 |   3468 |00:00:00.01 |     207 |
|*  3 |    INDEX RANGE SCAN                  | IX_CF10_SHP_YM |      1 |   3472 |   3468 |00:00:00.01 |      14 |
---------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("SHP_NO"=:SHP AND SUBSTR("SLE_DT",1,6)=:YM)

문제 ​

  1. 같은 SQL이 운영에서만 FULL로 도는 이유를 운영 V$SQL의 텍스트와 Predicate Information으로 설명하시오.
  2. FORCE를 그대로 둔 채 이 화면이 인덱스 범위 스캔을 하도록 SQL과 인덱스를 고치시오. 다른 방법을 하나 더 들고 그 대가를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자