Skip to content
튜닝 연습인덱스 스캔 효율과 테이블 액세스
ACC-020

인덱스를 정리한 다음 주, 분기 마감 상황판이 인덱스를 통째로 읽는다

실전SELECT한빛유통95 / 700

상황 ​

지난주 「취소 추이」 대시보드를 고치면서 IX_ORD_BIG_DT_ST (ORD_DT, ORD_ST)를 IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT)로 바꿨다(ACC-001). 화요일부터 금요일까지 V$SQL_PLAN을 관찰해 옛 인덱스를 쓰는 SQL이 없음을 확인하고 지웠다. 7월 1일 월요일, 운영팀의 「분기 마감 상황판」이 AWR의 'SQL ordered by Gets' 상위에 새로 올라왔다. 이 상황판은 분기가 끝난 다음 주에만 띄우고, 5분마다 새로 고쳐 하루 약 290번 실행된다. 지난 분기의 일별, 상태별 주문 건수를 보여 주며, 옛 인덱스로는 326블록이었다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 2024년 2분기 83,083건(COMPLETE 74,866 · PENDING 6,573 · CANCEL 1,644)
  • ORD_ST는 NULL 허용이고 제약이 없다 · 지금 값은 COMPLETE, PENDING, CANCEL 셋뿐이다(NULL 0건)
  • 인덱스 IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT): BLEVEL 2 · 리프 약 3,890블록(리프 한 블록에 약 257항목) · 통계 최신 · ORD_ST에 히스토그램
  • 지운 IX_ORD_BIG_DT_ST (ORD_DT, ORD_ST): 같은 크기 · 운영에서는 주문이 계속 INSERT된다(인덱스 하나에 5만 건당 redo 약 11.8MB, 교재 04화)

원본 SQL ​

sql
SELECT ORD_DT, ORD_ST, COUNT(*) AS ord_cnt
  FROM T_ORD_BIG
 WHERE ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
   AND ORD_DT <  TO_DATE(:v_to, 'YYYYMMDD') + 1
 GROUP BY ORD_DT, ORD_ST
 ORDER BY ORD_DT, ORD_ST

실행계획 학습용 예시 · 건수는 적재 스크립트 그대로 · 인덱스 규모는 ACC-001과 같다 ​

xplan
[지금 — 옛 인덱스를 지운 뒤 · 2024년 2분기]
------------------------------------------------------------------------------------------------------
| Id  | Operation             | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |                  |      1 |        |    127 |00:00:00.09 |    3904 |
|   1 |  SORT GROUP BY        |                  |      1 |    182 |    127 |00:00:00.09 |    3904 |
|*  2 |   FILTER              |                  |      1 |        |  83083 |00:00:00.07 |    3904 |
|*  3 |    INDEX FAST FULL SCAN| IX_ORD_BIG_ST_DT |      1 |  83083 |  83083 |00:00:00.06 |    3904 |
------------------------------------------------------------------------------------------------------
   2 - filter(TO_DATE(:V_TO,'YYYYMMDD')+1>TO_DATE(:V_FROM,'YYYYMMDD'))
   3 - filter(("ORD_DT">=TO_DATE(:V_FROM,'YYYYMMDD') AND "ORD_DT"<TO_DATE(:V_TO,'YYYYMMDD')+1))

[옛 인덱스 IX_ORD_BIG_DT_ST 가 있을 때] INDEX RANGE SCAN · access(ORD_DT 범위) · 326

문제 ​

  1. 옛 인덱스로는 326블록이던 SQL이 3,904블록을 읽게 된 이유를 새 인덱스의 컬럼 순서로 설명하시오. 관찰에서 이 SQL을 놓친 이유도 설명하시오.
  2. 대안을 넷 들어 비교하시오. ① ORD_ST를 IN 목록으로 채운다 ② INDEX SKIP SCAN에 맡긴다 ③ 옛 인덱스를 다시 만든다 ④ 그대로 둔다. 각각의 Buffers, 결과가 틀릴 위험, DML 비용을 쓰시오.
  3. 하나를 골라 SQL과 필요한 DDL을 작성하시오. 고른 방식이 기대는 전제를 데이터베이스가 보장하게 하는 방법도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자