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

POS 테이블의 비트맵을 지우고 따로 만든 리포트 마트에서도 자유 조합 리포트는 마트를 통째로 읽는다

실전DDL버거132 / 700

상황 ​

POS 테이블에 비트맵 인덱스를 만들었다가 점심 결제가 멈춘 일 뒤로, 버거 본사 BI팀은 매일 밤 리포트용 마트 DW_ORD_ITEM을 따로 만들어 리포트를 그쪽으로 옮겼다. BI팀은 요청이 올 때마다 마트에 B-tree 인덱스를 하나씩 만들어 지금 셋이다. 그런데 BI 도구의 자유 조합 리포트(S1)는 여전히 마트를 통째로 읽고, 점장 리포트(S2)도 700블록 넘게 읽는다. 박팀장은 "마트의 인덱스를 처음부터 다시 설계하고 밤 적재 절차까지 적어 와라"고 했다.

테이블과 인덱스 ​

  • DW_ORD_ITEM 주문상품 리포트 마트: 매일 02:00에 TRUNCATE 뒤 ORD_ITEM · ORD · SHOP · ITEM을 조인해 INSERT /*+ APPEND */로 다시 채운다 · 낮에는 읽기만 한다 · 2,472,717행 · 15,200블록 · ORD_DT 순으로 쌓인다
  • 컬럼과 분포: ORD_NO(약 100만 가지) · ORD_DT VARCHAR2(8) 730일 · ORD_HH 14가지(09 ~ 22시, 고르게) · DOW_CD 7가지 · SHOP_NO 200곳 · RGN_CD 8곳(매장이 고르게 나뉜다) · ITEM_ID 92가지 · ITEM_TYPE_CD 여러 분류(버거 '100100'이 18종, 약 20%) · ORD_ITEM_QTY
  • 지금 인덱스(B-tree): DW_OI_X01 (ORD_DT, SHOP_NO) · DW_OI_X02 (ITEM_ID, ORD_DT) · DW_OI_X03 (RGN_CD, ITEM_TYPE_CD) · 리프는 각각 약 9,200 · 9,200 · 7,100블록
  • 실행 횟수: S1 하루 약 400번(조합은 매번 다르다) · S2 하루 약 2,000번 · S3 하루 수십 번 · 밤 적재 1번(지금 02:00 ~ 03:10, 그중 인덱스 셋을 고치는 데 약 25분)

원본 SQL ​

sql
-- S1 자유 조합 리포트 (BI 도구가 고른 조건만 WHERE에 붙인다 · 아래는 한 예)
SELECT ITEM_ID, SUM(ORD_ITEM_QTY) AS qty, COUNT(*) AS line_cnt
  FROM DW_ORD_ITEM
 WHERE RGN_CD       = :v_rgn
   AND ITEM_TYPE_CD = '100100'
   AND DOW_CD       IN ('1', '7')
   AND ORD_HH       BETWEEN '11' AND '13'
 GROUP BY ITEM_ID;

-- S2 점장 리포트 : 매장 하나의 한 달 일별 메뉴 판매
SELECT ORD_DT, ITEM_ID, SUM(ORD_ITEM_QTY) AS qty
  FROM DW_ORD_ITEM
 WHERE SHOP_NO = :v_shop
   AND ORD_DT BETWEEN :v_from AND :v_to
 GROUP BY ORD_DT, ITEM_ID;

-- S3 고객센터 : 주문 한 건의 상품
SELECT ITEM_ID, ORD_ITEM_QTY FROM DW_ORD_ITEM WHERE ORD_NO = :v_ord_no;

-- 밤 적재 (02:00)
TRUNCATE TABLE DW_ORD_ITEM;
INSERT /*+ APPEND */ INTO DW_ORD_ITEM
SELECT ... ;
COMMIT;

실행계획 학습용 예시 · 마트는 ORD_ITEM에서 만든 파생 테이블 · 조합 건수는 컬럼이 고르게 분포한다고 보고 센 값 · 비트맵 블록 수는 어림값 ​

xplan
[S1 · 한 지역 · 버거 · 주말 · 11 ~ 13시]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |             |      1 |        |     18 |00:00:00.21 |   15200 |
|   1 |  HASH GROUP BY                       |             |      1 |     18 |     18 |00:00:00.21 |   15200 |
|*  2 |   TABLE ACCESS FULL                  | DW_ORD_ITEM |      1 |   3705 |   3705 |00:00:00.20 |   15200 |
-------------------------------------------------------------------------------------------------------------
   2 - filter(("RGN_CD"=:V_RGN AND "ITEM_TYPE_CD"='100100' AND INTERNAL_FUNCTION("DOW_CD")
              AND "ORD_HH">='11' AND "ORD_HH"<='13'))

[S2 · SH0042 · 2013년 11월]
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |             |      1 |        |    488 |00:00:00.02 |     737 |
|   1 |  HASH GROUP BY                       |             |      1 |    515 |    488 |00:00:00.02 |     737 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| DW_ORD_ITEM |      1 |    515 |    515 |00:00:00.02 |     737 |
|*  3 |    INDEX RANGE SCAN                  | DW_OI_X01   |      1 |    515 |    515 |00:00:00.01 |     385 |
-------------------------------------------------------------------------------------------------------------
   3 - access("ORD_DT">=:V_FROM AND "SHOP_NO"=:V_SHOP AND "ORD_DT"<=:V_TO)
       filter("SHOP_NO"=:V_SHOP)

문제 ​

  1. S1이 지금 인덱스 셋을 제대로 쓰지 못하고 FULL로 읽는 이유와, S2가 737블록을 읽는 이유를 설명하시오.
  2. S1, S2, S3와 밤 적재를 함께 보고 마트의 인덱스를 다시 설계하시오. 컬럼마다 비트맵으로 할지 B-tree로 할지, 지금 인덱스를 어떻게 할지 정하고 DDL과 밤 적재 절차를 작성하시오.
  3. 고친 설계에서 S1과 S2의 읽는 양을 어림하고, 이 비트맵 설계를 POS가 쓰는 운영 테이블에 그대로 옮기면 안 되는 이유를 설명하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자