Skip to content
튜닝 연습DML과 배치 튜닝
DML-049

고객센터 주문번호 찾기용 인덱스 하나를 더 지었을 뿐인데 야간 재적재가 5초에서 22초가 됐다

중급DDL한빛유통474 / 700

상황 ​

한빛유통 고객센터는 고객이 불러 주는 주문번호(HB-20240615-0012345 모양)로 주문을 찾는다. 주문번호는 저장된 칸이 아니라 주문일자와 주문 순번을 이어 붙인 표기다. 지난달 개발팀이 표기 함수 FN_ORD_NO를 만들고 그 함수로 함수 기반 인덱스를 만들었다. 체인 ①(T_ORD_BIG 재적재)은 14화 이후 인덱스를 끄고 직접 경로로 넣은 뒤 다시 짓는데, 새 인덱스도 그 순서에 넣었다. 그 뒤로 체인 ①이 5.0초에서 21.9초가 됐다. 오과장: "인덱스 하나를 더 지었을 뿐인데 다른 두 인덱스를 합친 것의 세 배가 넘게 걸려요."

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_SEQ 1 ~ 1,000,000(유일) · ORD_DT 전부 자정 · 체인 ①이 끝난 뒤에만 화면이 읽고, 낮에는 고치는 일이 없다.
  • 인덱스: IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT) · IX_ORD_BIG_DT_SEQ (ORD_DT, ORD_SEQ) · 지난달 만든 IX_ORD_BIG_ORDNO_FX (FN_ORD_NO(ORD_DT, ORD_SEQ))
  • FN_ORD_NO: PL/SQL 함수 · DETERMINISTIC · 테이블을 읽지 않고 'HB-' || 주문일자(YYYYMMDD) || '-' || 순번 7자리를 돌려준다.
  • 고객센터 「주문번호로 찾기」는 하루 약 2,400번이다. 주문번호 형식은 바뀐 적이 없고, 화면이 형식을 먼저 검사한다.
  • 체인 ①은 일요일 22:00에 시작해 04:00까지 끝나야 한다.

원본 SQL ​

sql
-- 지난달 개발팀
CREATE OR REPLACE FUNCTION FN_ORD_NO(p_dt DATE, p_seq NUMBER)
RETURN VARCHAR2 DETERMINISTIC IS
BEGIN
  RETURN 'HB-' || TO_CHAR(p_dt, 'YYYYMMDD') || '-' || LPAD(p_seq, 7, '0');
END;

CREATE INDEX IX_ORD_BIG_ORDNO_FX ON T_ORD_BIG (FN_ORD_NO(ORD_DT, ORD_SEQ));

-- 고객센터 「주문번호로 찾기」
SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT
  FROM T_ORD_BIG
 WHERE FN_ORD_NO(ORD_DT, ORD_SEQ) = :v_ord_no;

-- 체인 ① 끝부분 (14화 이후 순서에 한 줄을 더했다)
ALTER INDEX IX_ORD_BIG_DT_AMT REBUILD;
ALTER INDEX IX_ORD_BIG_DT_SEQ REBUILD;
ALTER INDEX IX_ORD_BIG_ORDNO_FX REBUILD;

실행계획 학습용 예시 · 함수 호출 수는 시험용 패키지 카운터로 센 값 ​

xplan
[고객센터 「주문번호로 찾기」]
------------------------------------------------------------------------------------------------------------
| Id  | Operation                   | Name                | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                     |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T_ORD_BIG           |      1 |      1 |      1 |00:00:00.01 |       4 |
|*  2 |   INDEX RANGE SCAN          | IX_ORD_BIG_ORDNO_FX |      1 |      1 |      1 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("SQLP"."FN_ORD_NO"("ORD_DT","ORD_SEQ")=:V_ORD_NO)

[체인 ① 단계별 — 연습 DB 복사본]
  단계                                    경과       CPU      PL/SQL 함수 호출
  TRUNCATE · UNUSABLE · APPEND 적재       0.3 초     0.2 초                0
  REBUILD IX_ORD_BIG_DT_AMT               2.4 초     2.1 초                0
  REBUILD IX_ORD_BIG_DT_SEQ               2.3 초     2.0 초                0
  REBUILD IX_ORD_BIG_ORDNO_FX            16.9 초    16.2 초        1,000,000
  합계                                   21.9 초                 (지난달 5.0 초)

[REBUILD IX_ORD_BIG_ORDNO_FX — 세션 통계]
  PL/SQL execution elapsed time   11.4 초
  sorts (rows)                    1,000,000
  리프 블록                        약 4,370 (키 19바이트 + ROWID)

문제 ​

  1. 인덱스 하나를 다시 짓는 데 16.9초가 걸리는 이유를 설명하시오.
  2. 「주문번호로 찾기」를 지금처럼 4블록 안팎으로 처리하면서 체인 ①을 원래 시간으로 돌려놓으시오.
  3. 같은 식을 SQL 내장 함수로 쓴 가상 컬럼에 인덱스를 만드는 방법과 비교해 대가를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자