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

같은 취소 주문 2만 건인데 남은 인덱스로 읽자 마감 점검의 테이블 블록이 5,826에서 2만으로 늘었다

실전SELECT한빛유통97 / 700

상황 ​

월말 마감의 「취소 주문 전수 점검」은 취소 주문 전부를 고객별로 묶어 원장과 맞춘다. 마감 날에는 단계마다 돌아서 하루 30번쯤 실행된다. 소스에는 /*+ INDEX(o (ORD_ST)) */ 힌트와 함께 "상태 인덱스를 꼭 타게 (2019 강도현)"라는 주석이 있다. 지난주 DBA가 접두 중복 규칙(교재 04화 §6)에 따라 IX_ORD_BIG_ST (ORD_ST)를 지웠다. "(ORD_ST, ORD_DT)의 접두부라서 하던 일은 그 인덱스가 대신한다." 실행계획을 보니 점검 SQL도 남은 인덱스를 탄다. 그런데 이번 마감에서 한 번에 5,878블록이던 점검이 20,074블록이 됐다. 박팀장은 이렇게 주문했다. "인덱스를 되살릴지, 다른 길이 있는지, 정리 규칙은 어떻게 고칠지 숫자로 적어 와라."

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록(블록당 약 172행) · ORD_SEQ 순으로 쌓였다 · ORD_DT 1,095일(하루 913건, 전부 자정)
  • 취소(ORD_ST = 'CANCEL') 20,000건(2%): ORD_SEQ가 50의 배수인 주문이다 · 같은 날짜의 취소 주문은 ORD_SEQ가 10,950씩 떨어져 있다 · 취소 주문이 있는 고객은 40명
  • 지운 인덱스 IX_ORD_BIG_ST (ORD_ST): BLEVEL 2 · 리프 약 2,500블록 · CLUSTERING_FACTOR 17,478
  • 남은 인덱스 IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT): 「취소 추이」 화면용(ACC-001의 처방) · BLEVEL 2 · 리프 약 3,610블록 · CLUSTERING_FACTOR 약 1,000,000
  • 그 밖에 IX_ORD_BIG_DT (ORD_DT) · 통계 최신 · ORD_ST에 히스토그램 · 주문은 운영에서 계속 INSERT된다(교재 04화 실측: 인덱스 하나에 5만 건 적재 redo 약 11.8MB)

원본 SQL ​

sql
SELECT /*+ INDEX(o (ORD_ST)) */
       o.CUS_ID, COUNT(*) AS cnl_cnt, SUM(o.ORD_AMT) AS cnl_amt
  FROM T_ORD_BIG o
 WHERE o.ORD_ST = 'CANCEL'
 GROUP BY o.CUS_ID

실행계획 학습용 예시 · 건수는 적재 스크립트 그대로 ​

xplan
[지난달 마감: IX_ORD_BIG_ST가 있을 때]
------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                  |      1 |        |     40 |00:00:00.06 |    5878 |
|   1 |  HASH GROUP BY                       |                  |      1 |   2000 |     40 |00:00:00.06 |    5878 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG        |      1 |  20000 |  20000 |00:00:00.05 |    5878 |
|*  3 |    INDEX RANGE SCAN                  | IX_ORD_BIG_ST    |      1 |  20000 |  20000 |00:00:00.01 |      52 |
------------------------------------------------------------------------------------------------------------------
   3 - access("O"."ORD_ST"='CANCEL')

[이번 마감: IX_ORD_BIG_ST를 지운 뒤]
------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name             | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                  |      1 |        |     40 |00:00:00.18 |   20074 |
|   1 |  HASH GROUP BY                       |                  |      1 |   2000 |     40 |00:00:00.18 |   20074 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG        |      1 |  20000 |  20000 |00:00:00.17 |   20074 |
|*  3 |    INDEX RANGE SCAN                  | IX_ORD_BIG_ST_DT |      1 |  20000 |  20000 |00:00:00.01 |      74 |
------------------------------------------------------------------------------------------------------------------
   3 - access("O"."ORD_ST"='CANCEL')

[같은 조건 · FULL 강제]  TABLE ACCESS FULL 5,826

문제 ​

  1. 같은 20,000행을 같은 모양(INDEX RANGE SCAN 다음 TABLE ACCESS BY INDEX ROWID)으로 읽는데 테이블 쪽 Buffers가 약 5,826과 20,000으로 갈리는 이유를, 두 인덱스 안에서 'CANCEL' 항목이 어떤 순서로 놓여 있는지를 근거로 설명하시오.
  2. 처방을 셋 이상 들어 Buffers와 대가를 비교하시오. 지운 인덱스를 되살리는 안도 비교에 넣고, 하나를 골라 SQL과 DDL을 작성하시오.
  3. "접두부 인덱스는 지워도 된다"는 정리 규칙에 무엇을 더해야 하는지, 이 SQL의 힌트 모양과 함께 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자