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

체인 맨 앞에서 통계를 한 번에 다 모았는데 월요일 아침 두 곳이 무너졌다

실전PLSQL한빛유통605 / 700

상황 ​

일요일 22:00 야간 체인은 네 단계다. 오과장이 체인을 정리하면서 ① 뒤의 통계 수집 단계를 뺐고("APPEND가 알아서 모은다"), 대신 체인 맨 앞(21:50)에 GATHER_SCHEMA_STATS 한 줄을 넣었다("한 번에 다 모으자"). 월말과 일요일이 겹친 6월 30일 밤 ④ 재처리가 15초 가까이 걸렸고, 7월 1일 월요일 09:00 정산팀의 「6월 취소 주문」 조회가 지난주보다 열 배 무거워졌다. 박팀장: "통계 정책을 체인 단위로 다시 짜 주세요. 테이블마다 언제, 어떻게, 왜."

테이블과 인덱스 ​

  • T_ORD_BIG: 1,000,000행 · 5,826블록 · 매주 일요일 재적재(원천 STG_ORD_BIG) · 인덱스 IX_ORD_BIG_DT_AMT (ORD_DT, ORD_AMT) · IX_ORD_BIG_ST_DT (ORD_ST, ORD_DT)(클러스터링 팩터 약 99만) · 선호도 FOR ALL COLUMNS SIZE 1 FOR COLUMNS SIZE 254 ORD_ST(정책서 1호)
  • T_ORD_STLM: 100,000행 · 640블록 · 인덱스 없음 · 체인마다 비우고 다시 싣는다 · 히스토그램이 필요한 열 없음
  • W_STLM_ERR 정산 오류 작업 테이블(이 문제용): ORD_SEQ · ERR_CD · 평소 0 ~ 300행, 월말 약 20,000행(64블록) · 인덱스 없음 · 체인 끝에 DELETE로 비운다
  • T_ORD: 100,000행 · 896블록 · PK_T_ORD · V_STLM_CHECK는 정산과 주문을 견주는 점검 뷰다.
  • GT_CUS_PICK: 상담 화면의 전역 임시 테이블 · 통계 없음 · M_CUS · M_RGN · M_ITM은 거의 바뀌지 않는다.
  • 자동 통계 수집 창은 체인과 겹치지 않게 일요일 20:00에 닫힌다.

원본 SQL ​

sql
-- 일요일 21:50 · 야간 체인 (오과장 정리판)
BEGIN
  -- "한 번에 다 모으자"
  DBMS_STATS.GATHER_SCHEMA_STATS(ownname => USER, no_invalidate => FALSE);

  -- ① T_ORD_BIG 재적재 (통계 수집 단계는 뺐다 : "APPEND 가 알아서 모은다")
  EXECUTE IMMEDIATE 'TRUNCATE TABLE T_ORD_BIG';
  INSERT /*+ APPEND */ INTO T_ORD_BIG (ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT)
  SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_ST, ORD_AMT FROM STG_ORD_BIG;
  COMMIT;

  -- ② T_ORD_STLM 정산 적재
  EXECUTE IMMEDIATE 'TRUNCATE TABLE T_ORD_STLM';
  INSERT /*+ APPEND */ INTO T_ORD_STLM (STLM_SEQ, ORD_SEQ, CUS_ID, STLM_DT, STLM_AMT)
  SELECT ROWNUM, ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT FROM T_ORD;
  COMMIT;

  -- ③ 정산 오류 건 (평소 0 ~ 300행 · 월말 약 20,000행)
  INSERT INTO W_STLM_ERR (ORD_SEQ, ERR_CD)
  SELECT ORD_SEQ, ERR_CD FROM V_STLM_CHECK;
  COMMIT;

  -- ④ 재처리 → 비우기
  UPDATE T_ORD_STLM s
     SET s.STLM_AMT = (SELECT o.ORD_AMT FROM T_ORD o WHERE o.ORD_SEQ = s.ORD_SEQ)
   WHERE s.ORD_SEQ IN (SELECT e.ORD_SEQ FROM W_STLM_ERR e);
  COMMIT;
  DELETE FROM W_STLM_ERR;
  COMMIT;
END;

실행계획 학습용 예시 · 교재 10화와 14화의 실측 숫자 ​

xplan
[USER_TAB_STATISTICS — 7월 1일(월) 09:00]
 TABLE_NAME    NUM_ROWS   LAST_ANALYZED   비고
 T_ORD_BIG    1,000,000   일 22:04        NOTES = STATS_ON_LOAD · ORD_ST HISTOGRAM NONE
 T_ORD_STLM     100,000   일 22:06        NOTES = STATS_ON_LOAD
 W_STLM_ERR           0   일 21:51        (체인 맨 앞의 GATHER_SCHEMA_STATS)
 T_ORD          100,000   일 21:50
 M_CUS            2,000   일 21:50

[A] 7월 1일(월) 09:00 · 「6월 취소 주문」
    SELECT ORD_SEQ, CUS_ID, ORD_DT, ORD_AMT FROM T_ORD_BIG
     WHERE ORD_ST = 'CANCEL' AND ORD_DT >= DATE '2024-06-01' AND ORD_DT < DATE '2024-07-01'
---------------------------------------------------------------------------------------
| Id  | Operation         | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |      1 |        |    549 |00:00:00.06 |    5826 |
|*  1 |  TABLE ACCESS FULL| T_ORD_BIG |      1 |   9445 |    549 |00:00:00.06 |    5826 |
---------------------------------------------------------------------------------------
   1 - filter(("ORD_ST"='CANCEL' AND "ORD_DT">=TO_DATE(' 2024-06-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')
              AND "ORD_DT"<TO_DATE(' 2024-07-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))
    (지난주 같은 조회 · ORD_ST FREQUENCY 가 있을 때 : E-Rows 567 · IX_ORD_BIG_ST_DT 범위 스캔 · Buffers 556)

[B] 6월 30일(일) 23:40 · ④ 재처리 (월말 · 오류 20,000건)
----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT             |            |      1 |        |      0 |00:00:14.82 |     12M |
|   1 |  UPDATE                      | T_ORD_STLM |      1 |        |      0 |00:00:14.82 |     12M |
|   2 |   NESTED LOOPS               |            |      1 |      1 |  20000 |00:00:14.60 |     12M |
|   3 |    SORT UNIQUE               |            |      1 |      1 |  20000 |00:00:00.02 |      64 |
|   4 |     TABLE ACCESS FULL        | W_STLM_ERR |      1 |      1 |  20000 |00:00:00.01 |      64 |
|*  5 |    TABLE ACCESS FULL         | T_ORD_STLM |  20000 |      1 |  20000 |00:00:14.55 |     12M |
|   6 |   TABLE ACCESS BY INDEX ROWID| T_ORD      |  20000 |      1 |  20000 |00:00:00.05 |   40000 |
|*  7 |    INDEX UNIQUE SCAN         | PK_T_ORD   |  20000 |      1 |  20000 |00:00:00.03 |   20000 |
----------------------------------------------------------------------------------------------
   5 - filter("S"."ORD_SEQ"="E"."ORD_SEQ")
   7 - access("O"."ORD_SEQ"=:B1)

문제 ​

  1. [A]와 [B]의 원인을 각각 그 테이블의 통계가 언제, 어떤 상태에서 모였는지로 설명하시오.
  2. 체인의 테이블마다(T_ORD_BIG, T_ORD_STLM, W_STLM_ERR, 마스터, GT_CUS_PICK) 통계를 언제 어떻게 모을지 정하고, 고친 체인을 쓰시오.
  3. 고른 방식마다의 대가와, 통계 수집 정책서에 남길 재검토 조건을 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자