테마
분기 누적 화면 사고 뒤에 한빛유통은 4월 16일 새벽부터 01:00 적재 배치 끝에서 T_ORD_BIG 통계를 매일 모은다. 그날 아침부터 영업 담당 40명이 쓰는 「지역 주간 주문」 화면(하루 약 3,000번)이 무거워졌다.
오과장은 이렇게 말했다. "매일 수집 때문이네요. 지난주 통계로 되돌리고 매일 수집은 빼죠." 선배는 고개를 저었다. "분기 화면은 그 수집 덕에 살아났어." 조민우가 AWR과 커서 캐시를 뽑았다.
T_ORD_BIG: 2024년 말까지 1,000,000행 + 2025년 1월부터 매일 01:00에 전날 주문 약 913건씩 날짜순으로 끝에 붙는다(4월 16일 현재 약 1,096,000행 · 6,386블록)IX_ORD_BIG_DT (ORD_DT)의 CLUSTERING_FACTOR가 약 1,000,000이다. 2025년 행은 테이블 끝 블록에 날짜순으로 몰려 있다(하루치가 6블록 안팎).M_CUS: 2,000행 · 30블록 · RGN_ID 8곳(지역마다 약 250명) · PK_M_CUS (CUS_ID)ORD_DT 최댓값 2025-03-30)-- 「지역 주간 주문」 (SQL_ID 4kq7c2v9x1m0d) : 지난 7일 주문을 지역 단위로
SELECT c.CUS_ID, c.CUS_NM, o.ORD_DT, o.ORD_ST, o.ORD_AMT
FROM M_CUS c, T_ORD_BIG o
WHERE c.RGN_ID = :rgn
AND o.CUS_ID = c.CUS_ID
AND o.ORD_DT >= TRUNC(SYSDATE) - 7
AND o.ORD_DT < TRUNC(SYSDATE)
ORDER BY o.ORD_DT DESC, c.CUS_ID;
-- 오과장 안 : 통계를 지난주로 되돌린다 (매일 수집도 뺀다)
BEGIN
DBMS_STATS.RESTORE_TABLE_STATS(USER, 'T_ORD_BIG',
as_of_timestamp => TIMESTAMP '2025-04-15 23:00:00',
no_invalidate => FALSE);
END;
/
[DBA_HIST_SQLSTAT · SQL_ID 4kq7c2v9x1m0d]
SNAP_ID 시작 PLAN_HASH_VALUE EXECUTIONS_DELTA BUFFER_GETS_DELTA 실행당 블록
9101 04-15 10:00 3528841177 262 23,318 89
9102 04-15 11:00 3528841177 275 24,475 89
9122 04-16 07:00 3528841177 31 2,759 89
9123 04-16 08:00 1201550986 248 1,594,640 6,430
9124 04-16 09:00 1201550986 281 1,806,830 6,430
[DBA_OPTSTAT_OPERATIONS]
04-16 01:12:07 ~ 01:12:09 gather_table_stats SQLP.T_ORD_BIG (적재 배치 · no_invalidate 기본값)
[V$SQL · 지금] 자식 0 하나 · PLAN_HASH_VALUE 1201550986 · LAST_LOAD_TIME 04-16 07:41 (3528841177 커서는 없다)
[지금 계획 · ALLSTATS LAST · :rgn = 3]
Plan hash value: 1201550986
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Used-Mem |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 801 |00:00:00.06 | 6430 | |
| 1 | SORT ORDER BY | | 1 | 799 | 801 |00:00:00.06 | 6430 | 83968 (0)|
|* 2 | HASH JOIN | | 1 | 799 | 801 |00:00:00.06 | 6430 | 1236K (0)|
|* 3 | TABLE ACCESS FULL| M_CUS | 1 | 250 | 250 |00:00:00.01 | 30 | |
|* 4 | TABLE ACCESS FULL| T_ORD_BIG | 1 | 6391 | 6391 |00:00:00.05 | 6400 | |
-------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."CUS_ID"="C"."CUS_ID")
3 - filter("C"."RGN_ID"=:RGN)
4 - filter(("O"."ORD_DT">=TRUNC(SYSDATE@!)-7 AND "O"."ORD_DT"<TRUNC(SYSDATE@!)))
[4월 15일의 계획 · DBMS_XPLAN.DISPLAY_AWR('4kq7c2v9x1m0d', 3528841177) · AWR에는 실행 통계와 조건절이 남지 않는다]
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 951 |
| 1 | SORT ORDER BY | | 114 | 951 |
| 2 | HASH JOIN | | 114 | 950 |
| 3 | TABLE ACCESS FULL | M_CUS | 250 | 11 |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| T_ORD_BIG | 913 | 938 |
| 5 | INDEX RANGE SCAN | IX_ORD_BIG_DT | 913 | 4 |
---------------------------------------------------------------------------------