Skip to content
튜닝 연습옵티마이저와 통계
OPT-060

야간 클레임 정산 INSERT에 지난주 베이스라인을 올렸는데 한 번도 쓰이지 않고 밤마다 계획이 오락가락한다

실전INSERT한빛유통650 / 700

상황 ​

정산팀 야간 배치 「클레임 취소 정산」은 01:20에 클레임 시스템에서 받은 고객 목록을 W_CLM_CUS에 싣고 바로 통계를 모은 뒤, 01:30에 그 고객들의 취소 주문을 T_CLM_STLM에 넣는다. 배치 로그를 보면 이 INSERT가 어떤 밤은 0.12초, 어떤 밤은 0.38초다. 지난주 오과장이 빨랐던 10월 3일의 계획을 AWR에서 꺼내 베이스라인으로 올렸지만, 그 뒤로도 느린 밤이 그대로 나온다. 배치 프레임워크는 장애 때 어느 실행의 SQL인지 찾으려고 모든 SQL 앞에 추적 주석을 붙여 보낸다(2024년 2월에 넣었다). 박팀장은 고칠 방법을 견줘 달라고 했다.

테이블과 인덱스 ​

  • W_CLM_CUS(작업 테이블 · M_CUS에서 파생): 매일 밤 비우고 다시 싣는다 · 40 ~ 300명 · 1 ~ 2블록 · 적재 직후 통계 수집
  • 클레임 고객에는 취소 고객 40명(고객당 500건, 전부 CANCEL)이 언제나 모두 들어 있고, 나머지는 취소 주문이 없는 고객이다
  • T_ORD_BIG: 1,000,000행 · 5,826블록 · ORD_ST에 FREQUENCY 히스토그램(CANCEL 2%) · IX_ORD_BIG_CUS_ST (CUS_ID, ORD_ST)
  • T_CLM_STLM(정산 클레임 · T_ORD_STLM과 같은 꼴): 인덱스 없음 · 날마다 2만 건 안팎이 쌓인다

원본 SQL ​

sql
-- 배치 프레임워크의 SQL 실행부 (모든 단계가 이 프로시저로 SQL 을 보낸다)
PROCEDURE RUN_STEP(p_job VARCHAR2, p_step VARCHAR2, p_body VARCHAR2) IS
  v_sql VARCHAR2(32767);
BEGIN
  v_sql := '/* JOB=' || p_job || ' STEP=' || p_step
        || ' RUN=' || TO_CHAR(SYSDATE, 'YYYYMMDD-HH24MISS') || ' */ ' || p_body;
  EXECUTE IMMEDIATE v_sql;
END;

-- 단계 02 의 본문 (p_body)
INSERT INTO T_CLM_STLM (CLM_DT, CUS_ID, ORD_SEQ, ORD_DT, ORD_AMT)
SELECT TRUNC(SYSDATE), o.CUS_ID, o.ORD_SEQ, o.ORD_DT, o.ORD_AMT
  FROM W_CLM_CUS w, T_ORD_BIG o
 WHERE o.CUS_ID = w.CUS_ID
   AND o.ORD_ST = 'CANCEL'

실행계획 학습용 예시 ​

xplan
[DBA_HIST_SQLSTAT · 단계 02 INSERT · 밤마다]
 날짜   SQL_ID          EXACT_MATCHING_SIGNATURE  PLAN_HASH_VALUE  클레임 고객  BUFFER_GETS  ELAPSED
 10-03  1a7k2c9x0m4qd   15226180973381201537         4190263358          300        6,479   0.12초
 10-04  9cd3v8n1q7w2s    8703361925470018214         2385510647           40       20,813   0.38초
 10-05  3mq0x6k2c8v1b   11902485617730264401         4190263358          180        6,477   0.12초
 10-06  6tz8d1w4k9c0n    2648100733152846627         2385510647           40       20,813   0.38초
 · 밤마다 SQL_ID와 서명이 다르다. V$SQL의 SQL_TEXT 앞부분: /* JOB=CLM_STLM STEP=02 RUN=20251006-013000 */ INSERT INTO T_CLM_STLM …

[DBA_SQL_PLAN_BASELINES]  10-07 09:10 오과장이 LOAD_PLANS_FROM_AWR로 올림 · SQL_ID 1a7k2c9x0m4qd · PLAN_HASH_VALUE 4190263358
 SIGNATURE 15226180973381201537 · ENABLED YES · ACCEPTED YES · LAST_EXECUTED (없음)

[느린 밤 · 10-06 · 클레임 고객 40명]
Plan hash value: 2385510647

----------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT                     |                   |      1 |        |      0 |00:00:00.38 |   20813 |
|   1 |  LOAD TABLE CONVENTIONAL             | T_CLM_STLM        |      1 |        |      0 |00:00:00.38 |   20813 |
|   2 |   NESTED LOOPS                       |                   |      1 |    400 |  20000 |00:00:00.29 |   20163 |
|   3 |    TABLE ACCESS FULL                 | W_CLM_CUS         |      1 |     40 |     40 |00:00:00.01 |       3 |
|   4 |    TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG        |     40 |     10 |  20000 |00:00:00.27 |   20160 |
|*  5 |     INDEX RANGE SCAN                 | IX_ORD_BIG_CUS_ST |     40 |     10 |  20000 |00:00:00.01 |     160 |
----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   5 - access("O"."CUS_ID"="W"."CUS_ID" AND "O"."ORD_ST"='CANCEL')

[빠른 밤 · 10-03 · 클레임 고객 300명]
Plan hash value: 4190263358

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |            |      1 |        |      0 |00:00:00.12 |    6479 |          |
|   1 |  LOAD TABLE CONVENTIONAL | T_CLM_STLM |      1 |        |      0 |00:00:00.12 |    6479 |          |
|*  2 |   HASH JOIN              |            |      1 |   3000 |  20000 |00:00:00.09 |    5829 | 1491K (0)|
|   3 |    TABLE ACCESS FULL     | W_CLM_CUS  |      1 |    300 |    300 |00:00:00.01 |       3 |          |
|*  4 |    TABLE ACCESS FULL     | T_ORD_BIG  |      1 |  20000 |  20000 |00:00:00.08 |    5826 |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("O"."CUS_ID"="W"."CUS_ID")
   4 - filter("O"."ORD_ST"='CANCEL')

문제 ​

  1. 10월 7일에 올린 베이스라인이 한 번도 쓰이지 않은 이유를 위 자료로 설명하시오. 클레임 고객이 40명인 밤에만 NL을 고르는 이유도 쓰시오.
  2. 네 가지 안(추적 주석을 세션 정보로 옮기고 베이스라인을 다시 올린다, 힌트로 조인 방법을 정한다, force_match 프로파일을 쓴다, 통계로 추정을 고친다)을 비교하시오.
  3. 고른 안으로 프레임워크와 단계 02를 고쳐 쓰시오. 그 대가와 대장에 남길 것도 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자