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

배포도 없었는데 월요일 아침 등급별 매출 화면이 30초 걸린다

기초PLSQL한빛유통591 / 700

상황 ​

08:41에 박팀장이 알려 왔다. "등급별 매출 화면이 오늘 아침부터 30초 걸린답니다. 어제 당직이 볼 때는 0.1초였고, 주말 사이 배포는 없었습니다." 화면의 SQL(Q2)은 06화에서 고친 그대로다. 일요일 22:30에 T_ORD_BIG 재적재(비우고 100만 건 다시 싣기)가 돌았다. 오과장은 이번 주부터 배치 맨 끝의 통계 수집 단계를 뺐다. 오과장의 설명은 이렇다. "인덱스가 셋이 되고 04시를 넘겨요. 자동 통계 수집이 밤마다 도니까 중복이죠."

테이블과 인덱스 ​

  • T_ORD_BIG: 실제 1,000,000행 · 5,826블록 · 통계 NUM_ROWS 0 · BLOCKS 0 · 마지막 수집 일요일 22:33 · STALE_STATS NO
  • M_CUS: 2,000행 · 30블록 · PK_M_CUS (CUS_ID)
  • M_RGN: 8행 · PK_M_RGN (RGN_ID)
  • 자동 통계 수집 창: 평일 22:00부터 4시간 · 토요일과 일요일은 06:00부터 20시간 · STALE인 객체만 · 선호도대로 · 인덱스까지
  • TRUNCATE하는 순간 테이블은 STALE_STATS = YES가 된다. 통계 이력은 31일 동안 보존한다.

원본 SQL ​

sql
-- 일요일 밤 재적재 (오과장 · 이번 주 판) — 원천 사본에서 100만 건을 다시 싣는다 (일반 INSERT)
BEGIN
  EXECUTE IMMEDIATE 'TRUNCATE TABLE T_ORD_BIG';
  INSERT 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 T_ORD_BIG_SRC;
  COMMIT;
  -- 2019-09-02 hd.kang : 적재 직후 통계 수집. 빼지 말 것.
  -- DBMS_STATS.GATHER_TABLE_STATS(USER, 'T_ORD_BIG');     ← 이번 주에 뺐다 (04시를 넘겨서)
END;
/

-- (참고) 느려진 화면 Q2 — SQL 은 06화 그대로다. 고치지 않는다
SELECT c.CUS_GD, COUNT(*) AS ord_cnt, SUM(o.ORD_AMT) AS amt,
       MAX((SELECT r.RGN_NM FROM M_RGN r WHERE r.RGN_ID = c.RGN_ID)) AS rgn_sample
  FROM T_ORD_BIG o, M_CUS c
 WHERE c.CUS_ID = o.CUS_ID
 GROUP BY c.CUS_GD;

실행계획 실측 · 교재 10화(복사본 재현) ​

xplan
[월요일 09:40 — 통계와 실제]
 TABLE_NAME   NUM_ROWS  BLOCKS  LAST_ANALYZED   STALE_STATS   실제 COUNT(*)
 T_ORD_BIG           0       0  일 22:33:36     NO               1,000,000

[Q2 — 월요일 아침 (복사본에서 일요일 밤의 순서를 그대로 재현)]
Plan hash value: 2289018667

---------------------------------------------------------------------------------------------
| Id  | Operation                     | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |           |      1 |        |      3 |00:00:00.79 |    1026K|
|   1 |  TABLE ACCESS BY INDEX ROWID  | M_RGN     |      8 |      1 |      8 |00:00:00.01 |      12 |
|*  2 |   INDEX UNIQUE SCAN           | PK_M_RGN  |      8 |      1 |      8 |00:00:00.01 |       4 |
|   3 |  HASH GROUP BY                |           |      1 |      1 |      3 |00:00:00.79 |    1026K|
|   4 |   NESTED LOOPS                |           |      1 |      1 |   1000K|00:00:00.76 |    1026K|
|   5 |    NESTED LOOPS               |           |      1 |      1 |   1000K|00:00:00.42 |   26840 |
|   6 |     TABLE ACCESS FULL         | T_ORD_BIG |      1 |      1 |   1000K|00:00:00.13 |    5829 |
|*  7 |     INDEX UNIQUE SCAN         | PK_M_CUS  |   1000K|      1 |   1000K|00:00:00.15 |   21011 |
|   8 |    TABLE ACCESS BY INDEX ROWID| M_CUS     |   1000K|      1 |   1000K|00:00:00.21 |    1000K|
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("R"."RGN_ID"=:B1)
   7 - access("C"."CUS_ID"="O"."CUS_ID")

[같은 Q2 · 지난 일요일 통계 (재적재 전에 모은 1,000,000 행)]  HASH JOIN · Buffers 5,868 · 0.18 초

문제 ​

  1. 코드가 그대로인데 계획이 바뀐 이유를 실행계획의 어느 칸에서 알 수 있는지 쓰시오. 통계가 언제 0이 되었는지도 쓰시오.
  2. 오늘 아침 운영에서 할 응급 조치와, 다음 일요일에 같은 일이 생기지 않도록 고친 배치를 쓰시오. 통계를 잠그는 안과도 비교하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자