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

2년 전에 고쳐 놓은 월마감 집계가 다시 느리다

실전SELECT버거77 / 700

상황 ​

버거원 본사의 월마감 화면 「가맹점별 주문 건수」가 다시 느려졌다. 2년 전 외부 점검 보고서(강도현)에는 "ORD_DT 선두 인덱스 RANGE SCAN으로 변경 완료" 한 줄만 있고 숫자가 없다. 1년 전에는 서버를 바꾸면서 주문을 다시 적재했다. 운영본은 ORD_RANDOM이고, 이관 전 보관본은 ORD다. 같은 SQL을 테이블 이름만 바꿔 양쪽에서 쟀다. 박팀장은 이렇게 주문했다. "월말에 본사 전 부서가 이 화면을 연다. 무엇을 할지, 대가까지 적어 와라."

테이블과 인덱스 ​

  • ORD_RANDOM(운영본): 1,000,000행 · 22,414블록 · 서버 교체 때 ORDER BY 없이 다시 적재
  • ORD(이관 전 보관본): 1,000,000행 · 22,422블록 · 주문 시각 순으로 쌓였다 · 내용은 ORD_RANDOM과 같다
  • ORD_DT VARCHAR2(8) '20120101' ~ '20131231' · 한 달 약 41,666건(4.17%) · SHOP_NO 200개 매장
  • 인덱스 ORD_RANDOM_X01 (ORD_DT, ORD_HMS) · ORD_X01 (ORD_DT, ORD_HMS): 정의가 같다 · 통계 최신

원본 SQL ​

sql
SELECT /*+ INDEX(o ORD_RANDOM_X01) */
       o.SHOP_NO, COUNT(*) AS ord_cnt
  FROM ORD_RANDOM o
 WHERE o.ORD_DT BETWEEN '20120101' AND '20120131'
 GROUP BY o.SHOP_NO

실행계획 실측 · 교재 04화 ​

xplan
[ORD_RANDOM — 운영본]
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |    200 |00:00:00.05 |   41825 |
|   1 |  HASH GROUP BY                       |                |      1 |    200 |    200 |00:00:00.05 |   41825 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| ORD_RANDOM     |      1 |   4065 |  41666 |00:00:00.04 |   41825 |
|*  3 |    INDEX RANGE SCAN                  | ORD_RANDOM_X01 |      1 |   4065 |  41666 |00:00:00.01 |     160 |
-----------------------------------------------------------------------------------------------------------------
   3 - access("O"."ORD_DT">='20120101' AND "O"."ORD_DT"<='20120131')

[ORD — 이관 전 보관본 · 같은 SQL (힌트의 인덱스 이름만 ORD_X01)]
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |      1 |        |    200 |00:00:00.02 |    1086 |
|   1 |  HASH GROUP BY                       |                |      1 |    200 |    200 |00:00:00.02 |    1086 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| ORD            |      1 |   4065 |  41666 |00:00:00.02 |    1086 |
|*  3 |    INDEX RANGE SCAN                  | ORD_X01        |      1 |   4065 |  41666 |00:00:00.01 |     160 |
-----------------------------------------------------------------------------------------------------------------
   3 - access("O"."ORD_DT">='20120101' AND "O"."ORD_DT"<='20120131')

[같은 조건 · FULL 강제]  ORD_RANDOM 22,234 · ORD 22,239

[딕셔너리 — USER_INDEXES]
INDEX_NAME       BLEVEL LEAF_BLOCKS   NUM_ROWS DISTINCT_KEYS CLUSTERING_FACTOR
---------------- ------ ----------- ---------- ------------- -----------------
ORD_RANDOM_X01        2        3774    1000000        986510            999958
ORD_X01               2        3774    1000000        986510             22223

문제 ​

  1. SQL과 인덱스 정의, 행수가 모두 같은데 Buffers가 41,825 대 1,086으로 갈리는 이유를 실행계획의 어느 줄과 딕셔너리의 어느 숫자로 알 수 있는지 설명하시오. 강도현의 보고서가 그 당시에 틀렸는지도 판단하시오.
  2. 처방을 셋 이상 들어 효과(Buffers)와 대가, 효과가 오래 가는지를 비교하시오. 그중 하나를 골라 SQL과 DDL을 작성하시오. ALTER INDEX … REBUILD가 처방이 아닌 이유도 설명하시오.
  3. 이 테이블에서 ORD_DT 범위가 며칠을 넘으면 인덱스 경로가 FULL보다 불리해지는지, 그 값을 어떻게 구하는지 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자