Skip to content
튜닝 연습인덱스 사용 조건
IDX-067

고객 인덱스 둘을 하나로 합치자는 안을 재 보니 상담 화면 첫 칸이 24블록에서 505블록으로 늘었다

중급DDL한빛유통67 / 700

상황 ​

고객센터 상담 화면은 고객 번호를 넣으면 세 칸을 띄운다. 이 세 SQL을 위해 T_ORD_BIG에는 고객 번호로 시작하는 인덱스가 둘 있다. 오과장은 "주문이 들어올 때마다 고객 인덱스를 둘씩 고친다. 하나로 합칠 수 없나?"라고 물었다. 조민우는 (CUS_ID, ORD_ST, ORD_DT) 하나로 합치자고 했다. INVISIBLE로 만들어 세션에서 재 보니 S2와 S3은 거의 그대로인데 S1이 24블록에서 505블록으로 늘었다.

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · CUS_ID 2,000명(고객당 500건, 한 고객의 주문은 테이블 곳곳의 서로 다른 블록에 있다) · ORD_ST COMPLETE 90% · PENDING 8% · CANCEL 2%(고객마다 섞여 있다) · ORD_DT 전부 자정
  • IX_ORD_BIG_CUS_DT (CUS_ID, ORD_DT): BLEVEL 2 · 리프 3,640블록 · S1, S3이 쓴다
  • IX_ORD_BIG_CUS_ST (CUS_ID, ORD_ST): ACC-039에서 S2용으로 만든 인덱스 · BLEVEL 2 · 리프 약 3,580블록
  • 조민우 안 IX_ORD_BIG_CUS_ST_DT (CUS_ID, ORD_ST, ORD_DT): INVISIBLE · BLEVEL 2 · 리프 약 4,350블록
  • 고객 한 명의 항목 500개는 어느 인덱스에서나 리프 2 ~ 3블록에 모여 있다
  • 그 밖에 IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT) · 주문은 운영에서 계속 INSERT된다(인덱스 하나에 5만 건당 redo 약 12MB, 교재 14화)

원본 SQL ​

sql
-- S1 「최근 주문 20건」 상담 화면 첫 칸 (하루 약 1만 번)
SELECT *
  FROM (SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
          FROM T_ORD_BIG
         WHERE CUS_ID = :v_cus
         ORDER BY ORD_DT DESC)
 WHERE ROWNUM <= 20;

-- S2 「고객 주문 상태 요약」 (하루 약 3천 번)
SELECT ORD_ST, COUNT(*) AS ord_cnt
  FROM T_ORD_BIG
 WHERE CUS_ID = :v_cus
 GROUP BY ORD_ST;

-- S3 「기간 주문 이력」 (하루 약 2천 번)
SELECT ORD_SEQ, ORD_DT, ORD_ST, ORD_AMT
  FROM T_ORD_BIG
 WHERE CUS_ID = :v_cus
   AND ORD_DT >= TO_DATE(:v_from, 'YYYYMMDD')
 ORDER BY ORD_DT DESC;

-- 조민우 안 : 재 보고 문제가 없으면 IX_ORD_BIG_CUS_DT, IX_ORD_BIG_CUS_ST 를 지운다
CREATE INDEX IX_ORD_BIG_CUS_ST_DT ON T_ORD_BIG (CUS_ID, ORD_ST, ORD_DT) INVISIBLE;

실행계획 학습용 예시 ​

xplan
[S1 · 지금 · CUS_000123]
------------------------------------------------------------------------------------------------------------
| Id  | Operation                      | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |                   |      1 |        |     20 |00:00:00.01 |      24 |
|*  1 |  COUNT STOPKEY                 |                   |      1 |        |     20 |00:00:00.01 |      24 |
|   2 |   VIEW                         |                   |      1 |     20 |     20 |00:00:00.01 |      24 |
|   3 |    TABLE ACCESS BY INDEX ROWID | T_ORD_BIG         |      1 |    500 |     20 |00:00:00.01 |      24 |
|*  4 |     INDEX RANGE SCAN DESCENDING| IX_ORD_BIG_CUS_DT |      1 |    500 |     20 |00:00:00.01 |       4 |
------------------------------------------------------------------------------------------------------------
   1 - filter(ROWNUM<=20)
   4 - access("CUS_ID"=:V_CUS)

[S1 · 조민우 안 (세션에서 잰 것) · CUS_000123]
-------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                 | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                      |      1 |        |     20 |00:00:00.01 |     505 |
|*  1 |  COUNT STOPKEY                         |                      |      1 |        |     20 |00:00:00.01 |     505 |
|   2 |   VIEW                                 |                      |      1 |    500 |     20 |00:00:00.01 |     505 |
|*  3 |    SORT ORDER BY STOPKEY               |                      |      1 |    500 |     20 |00:00:00.01 |     505 |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG            |      1 |    500 |    500 |00:00:00.01 |     505 |
|*  5 |      INDEX RANGE SCAN                  | IX_ORD_BIG_CUS_ST_DT |      1 |    500 |    500 |00:00:00.01 |       5 |
-------------------------------------------------------------------------------------------------------------------
   1 - filter(ROWNUM<=20)
   3 - filter(ROWNUM<=20)
   5 - access("CUS_ID"=:V_CUS)

[S2 · 조민우 안]  INDEX RANGE SCAN · access("CUS_ID"=:V_CUS) · 인덱스만 · Buffers 5 (지금 IX_ORD_BIG_CUS_ST로 4)
[S3 · 조민우 안]  INDEX RANGE SCAN · access("CUS_ID"=:V_CUS) filter("ORD_DT">=…) · 테이블 42행과 정렬 · Buffers 47 (지금 46)

문제 ​

  1. 조민우 안에서 S1이 24블록에서 505블록으로 늘어난 이유를, 인덱스 안에서 한 고객의 항목이 놓인 순서로 설명하시오.
  2. 인덱스 하나로 S1 ~ S3을 모두 받치는 컬럼 순서를 정하고, 그 인덱스에서 S2와 S3이 각각 어떻게 처리되는지 설명하시오.
  3. 바꾸는 DDL과 반영 절차를 작성하고, 합쳐서 얻는 것과 잃는 것을 숫자로 적으시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자