테마
본사 유통 MD 화면 「상품별 매장 판매」는 상품 번호를 넣으면 매장별 판매 수량과 금액을 보여 준다. 화면은 패키지 프로시저를 부르고, 프로시저 안의 정적 SQL이 상품 번호를 바인드로 받는다. MD 20명이 하루 약 900번 열고, 그 가운데 약 360번이 판매 1위와 2위 상품(1001, 1002)이다. 3월에 아침 첫 조회의 값에 따라 하루 종일 계획이 갈리는 사고가 난 뒤, DBA는 이 SQL을 해시 조인 계획 하나만 받아들인 베이스라인으로 지켜 왔다. 10월 6일 본사가 Tuning Pack을 계약했고, DBA는 그날 2019년부터 꺼 두었던 자동 SQL 튜닝 작업을 다시 켰다. 10월 7일부터 이틀에 하루꼴로 인기 상품 조회가 하루 종일 2초씩 걸린다. 박팀장은 지금 할 일과 다시 생기지 않게 할 일을 함께 정하자고 했다.
CF11 판매상품: 지금 1,118,268행 · 8,365블록(올해 신상품 판매가 더해졌다) · IX_CF11_ITM (ITM_NO): BLEVEL 2 · 리프 약 2,200 · ITM_NO에 FREQUENCY 히스토그램CF11은 매일 01:00 적재 뒤 통계를 다시 모은다(no_invalidate => FALSE). 그래서 이 SQL은 아침 첫 조회 때 하드 파싱한다.CF10 판매: 지금 509,800행 · 4,673블록 · CF10_PK (SLE_NO): BLEVEL 2 · 판매하는 매장 5곳optimizer_use_sql_plan_baselines TRUE · 자동 캡처는 꺼져 있다SYS_AUTO_SPM_EVOLVE_TASK의 설정은 한 번도 바꾼 적이 없다-- 화면이 부르는 프로시저 (정적 SQL · SQL_ID 7w2c5n0q8k4xd)
PROCEDURE ITEM_SHOP_SALES(p_itm IN NUMBER, p_tab OUT T_SHOP_SALES_TAB) IS
BEGIN
SELECT T_SHOP_SALES(h.SHP_NO, SUM(s.SLE_QTY), SUM(s.SLE_QTY * s.SLE_UPRC))
BULK COLLECT INTO p_tab
FROM CF11 s, CF10 h
WHERE s.ITM_NO = p_itm
AND h.SLE_NO = s.SLE_NO
GROUP BY h.SHP_NO;
END;
-- 2025-03-04 DBA : 해시 계획(2271840516) 하나만 받아들인 베이스라인으로 올렸다
DECLARE
n PLS_INTEGER;
BEGIN
n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id => '7w2c5n0q8k4xd',
plan_hash_value => 2271840516);
END;
/
-- 2025-10-06 DBA : Tuning Pack 계약에 맞춰 자동 SQL 튜닝 작업을 다시 켰다
BEGIN
DBMS_AUTO_TASK_ADMIN.ENABLE(client_name => 'sql tuning advisor', operation => NULL, window_name => NULL);
END;
/
[DBA_SQL_PLAN_BASELINES · SQL_ID 7w2c5n0q8k4xd]
PLAN_NAME ORIGIN ENABLED ACCEPTED CREATED LAST_VERIFIED 계획
SQL_PLAN_6s7m9k4c1e2b5a61e4f2 MANUAL-LOAD-FROM-CURSOR-CACHE YES YES 25-03-04 - HASH JOIN (CF11 FULL · CF10 FULL) · 2271840516
SQL_PLAN_6s7m9k4c1e2b5c93b7d5 AUTO-CAPTURE YES YES 25-03-05 25-10-06 22:14 NESTED LOOPS (IX_CF11_ITM → CF10_PK) · 3658024197
[DBA_ADVISOR_EXECUTIONS · SYS_AUTO_SPM_EVOLVE_TASK] 첫 실행 25-10-06 22:00 · 그 뒤 매일 22:00 · 그 전 기록 없음
[DBA_ADVISOR_PARAMETERS · SYS_AUTO_SPM_EVOLVE_TASK] ACCEPT_PLANS = TRUE (기본값)
[DBMS_SPM.REPORT_AUTO_EVOLVE_TASK · 10-06 22:00 실행 · 이 SQL 부분만]
Plan: SQL_PLAN_6s7m9k4c1e2b5c93b7d5
Base Plan (HASH) Test Plan (NL)
Elapsed Time (s) .2480 .0060
Buffer Gets 13,038 558
Rows Processed 5 5
FINDINGS
1. The plan was verified in 0.31000 seconds. It passed the benefit criterion because its verified
performance was 22.91347 times better than that of the baseline plan.
2. The plan was automatically accepted.
(시험 실행의 바인드 :B1 = 1096 · 그 계획이 3월 5일 대기 줄에 들어올 때 엿본 값)
[DBA_HIST_SQLSTAT · 7w2c5n0q8k4xd · 날마다]
날짜 아침 첫 조회의 :B1 그날 계획 EXECUTIONS BUFFER_GETS ELAPSED(초)
10-06 1002 2271840516 904 11,786,352 224
10-07 1109 3658024197 911 281,947,205 742
10-08 1001 2271840516 897 11,695,086 221
10-09 1096 3658024197 906 280,316,844 737
10-10 1003 2271840516 893 11,642,934 220
[10-09 10:21 · :B1 = 1001 · 그날 08:02 첫 조회(1096)로 만든 커서]
Plan hash value: 3658024197
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 5 |00:00:02.11 | 834K |
| 1 | HASH GROUP BY | | 1 | 5 | 5 |00:00:02.11 | 834K |
| 2 | NESTED LOOPS | | 1 | 140 | 275K|00:00:01.98 | 834K |
| 3 | NESTED LOOPS | | 1 | 140 | 275K|00:00:01.12 | 559K |
| 4 | TABLE ACCESS BY INDEX ROWID BATCHED| CF11 | 1 | 140 | 275K|00:00:00.31 | 8697 |
|* 5 | INDEX RANGE SCAN | IX_CF11_ITM | 1 | 140 | 275K|00:00:00.06 | 552 |
|* 6 | INDEX UNIQUE SCAN | CF10_PK | 275K| 1 | 275K|00:00:00.58 | 550K |
| 7 | TABLE ACCESS BY INDEX ROWID | CF10 | 275K| 1 | 275K|00:00:00.71 | 275K |
--------------------------------------------------------------------------------------------------------------
Peeked Binds (identified by position):
--------------------------------------
1 - :B1 (NUMBER): 1096
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("S"."ITM_NO"=:B1)
6 - access("H"."SLE_NO"="S"."SLE_NO")
[같은 SQL을 두 계획으로 잰 Buffers · 시험 DB(운영 복제)]
:B1 판매 줄 HASH (2271840516) NL (3658024197)
1001 275,135 13,038 834,102
1002 202,156 13,038 614,873
1003 30,870 13,038 100,772
1031 3,032 13,038 11,647
1109 217 13,038 864
1096 140 13,038 558